diff --git a/.circleci/config.yml b/.circleci/config.yml index 089b13c337f..1467e9e24bf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -53,6 +53,7 @@ orbs: gcp-cli: circleci/gcp-cli@3.0.1 slack: circleci/slack@4.10.1 shellcheck: circleci/shellcheck@3.2.0 + codecov: codecov/codecov@5.0.3 commands: gcp-oidc-authenticate: description: "Authenticate with GCP using a CircleCI OIDC token." @@ -202,10 +203,10 @@ jobs: gotestsum --format=testname --junitfile=../tmp/test-results/cannon-32.xml --jsonfile=../tmp/testlogs/log-32.json \ -- -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-32.out ./... working_directory: cannon - - run: - name: Upload Cannon coverage - command: codecov --verbose --clean --flags cannon-go-tests-32 -f ./coverage-32.out - working_directory: cannon + - codecov/upload: + disable_search: true + files: ./cannon/coverage-32.out + flags: cannon-go-tests-32 - when: condition: equal: [64, <>] @@ -217,10 +218,10 @@ jobs: gotestsum --format=testname --junitfile=../tmp/test-results/cannon-64.xml --jsonfile=../tmp/testlogs/log-64.json \ -- --tags=cannon64 -parallel=$(nproc) -coverpkg=github.com/ethereum-optimism/optimism/cannon/... -coverprofile=coverage-64.out ./... working_directory: cannon - - run: - name: Upload Cannon coverage - command: codecov --verbose --clean --flags cannon-go-tests-64 -f ./coverage-64.out - working_directory: cannon + - codecov/upload: + disable_search: true + files: ./cannon/coverage-64.out + flags: cannon-go-tests-64 - store_test_results: path: ./tmp/test-results - store_artifacts: @@ -257,7 +258,7 @@ jobs: command: | # Clone asterisc @ the pinned version to fetch remote `RISCV.sol` ASTERISC_REV="$(cat ../../versions.json | jq -r .asterisc)" - REMOTE_ASTERISC_PATH="./src/asterisc/RISCV_Remote.sol" + REMOTE_ASTERISC_PATH="./src/vendor/asterisc/RISCV_Remote.sol" git clone https://github.com/ethereum-optimism/asterisc \ -b $ASTERISC_REV && \ cp ./asterisc/rvsol/src/RISCV.sol $REMOTE_ASTERISC_PATH @@ -606,11 +607,10 @@ jobs: environment: FOUNDRY_PROFILE: ci working_directory: packages/contracts-bedrock - - run: - name: upload coverage - command: codecov --verbose --clean --flags contracts-bedrock-tests - environment: - FOUNDRY_PROFILE: ci + - codecov/upload: + disable_search: true + files: ./packages/contracts-bedrock/coverage/lcov.info + flags: contracts-bedrock-tests contracts-bedrock-tests: docker: @@ -703,6 +703,10 @@ jobs: FOUNDRY_PROFILE: ci working_directory: packages/contracts-bedrock when: on_fail + - run: + name: Lint forge test names + command: just lint-forge-tests-check-no-build + working_directory: packages/contracts-bedrock - save_cache: name: Save Go build cache key: golang-build-cache-contracts-bedrock-tests-{{ checksum "go.sum" }} @@ -750,22 +754,7 @@ jobs: - run-contracts-check: command: unused-imports-check-no-build - run-contracts-check: - command: lint-forge-tests-check-no-build - - contracts-bedrock-validate-spacers: - docker: - - image: <> - resource_class: medium - steps: - - checkout - - attach_workspace: { at: "." } - - install-contracts-dependencies - - check-changed: - patterns: contracts-bedrock - - run: - name: validate spacers - command: just validate-spacers-no-build - working_directory: packages/contracts-bedrock + command: validate-spacers-no-build todo-issues: parameters: @@ -878,116 +867,84 @@ jobs: path: tmp/testlogs when: always - go-test: + go-tests: + parameters: + notify: + description: Whether to notify on failure + type: boolean + default: false + mentions: + description: Slack user or group to mention when notifying of failures + type: string + default: "" + resource_class: + description: Machine resource class + type: string + default: ethereum-optimism/latitude-1-go-e2e + no_output_timeout: + description: Timeout for when CircleCI kills the job if there's no output + type: string + default: 60m + test_timeout: + description: Timeout for running tests + type: string + default: 10m + environment_overrides: + description: Environment overrides + type: string + default: "" + packages: + description: List of packages to test + type: string machine: true - resource_class: ethereum-optimism/latitude-1 + resource_class: <> steps: - checkout - attach_workspace: at: "." + - run: + name: build op-program-client + command: make op-program-client + working_directory: op-program - run: name: run tests + no_output_timeout: <> command: | mkdir -p ./tmp/test-results && mkdir -p ./tmp/testlogs + cd op-e2e && make pre-test && cd .. + packages=( - op-batcher - op-chain-ops - op-node - op-proposer - op-challenger - op-dispute-mon - op-conductor - op-program - op-service - op-supervisor - op-deployer + <> ) formatted_packages="" for package in "${packages[@]}"; do formatted_packages="$formatted_packages ./$package/..." done + export OP_E2E_CANNON_ENABLED="false" + export OP_E2E_SKIP_SLOW_TEST=true + export OP_E2E_USE_HTTP=true export ENABLE_ANVIL=true export SEPOLIA_RPC_URL="https://ci-sepolia-l1-archive.optimism.io" export MAINNET_RPC_URL="https://ci-mainnet-l1-archive.optimism.io" + <> + gotestsum --format=testname \ --junitfile=./tmp/test-results/results.xml \ --jsonfile=./tmp/testlogs/log.json \ - -- -coverpkg=github.com/ethereum-optimism/optimism/... \ - -coverprofile=coverage.out $formatted_packages + --rerun-fails=2 \ + --packages="$formatted_packages" \ + -- -coverprofile=coverage.out -timeout=<> + - codecov/upload: + disable_search: true + files: ./coverage.out - store_test_results: path: ./tmp/test-results - store_artifacts: path: ./tmp/testlogs when: always - - go-e2e-test: - parameters: - module: - description: Go Module Name - type: string - target: - description: The make target to execute - type: string - notify: - description: Whether to notify on failure - type: boolean - default: false - mentions: - description: Slack user or group to mention when notifying of failures - type: string - default: "" - resource_class: - description: Machine resource class - type: string - default: ethereum-optimism/latitude-1 - skip_slow_tests: - description: Indicates that slow tests should be skipped - type: boolean - default: false - machine: true - resource_class: <> - steps: - - checkout - - attach_workspace: - at: ./tmp/workspace - - run: - name: Load devnet-allocs and artifacts - command: | - mkdir -p .devnet - cp -r ./tmp/workspace/.devnet* . - cp -r ./tmp/workspace/packages/contracts-bedrock/forge-artifacts packages/contracts-bedrock/forge-artifacts - cp ./tmp/workspace/packages/contracts-bedrock/deploy-config/devnetL1.json packages/contracts-bedrock/deploy-config/devnetL1.json - cp -r ./tmp/workspace/packages/contracts-bedrock/deployments/devnetL1 packages/contracts-bedrock/deployments/devnetL1 - - run: - name: print go's available MIPS targets - command: go tool dist list | grep mips - - run: - name: run tests - no_output_timeout: 20m - command: | - mkdir -p ./tmp/testlogs - mkdir -p ./tmp/test-results - - # The below env var gets overridden when running make test-cannon, but we - # need to explicitly set it here to prevent Cannon from running when we don't - # want it to. - export OP_E2E_CANNON_ENABLED="false" - export OP_E2E_SKIP_SLOW_TEST=<> - # Note: We don't use circle CI test splits because we need to split by test name, not by package. There is an additional - # constraint that gotestsum does not currently (nor likely will) accept files from different packages when building. - JUNIT_FILE=../tmp/test-results/<>_<>.xml JSON_LOG_FILE=../tmp/testlogs/test.log make <> - working_directory: <> - - store_artifacts: - path: ./tmp/testlogs - when: always - - store_artifacts: - path: ./tmp/test-results - when: always - - store_test_results: - path: ./tmp/test-results - when: condition: "<>" steps: @@ -1163,6 +1120,7 @@ jobs: - run: echo Done fpp-verify: + circleci_ip_ranges: true docker: - image: cimg/go:1.21 steps: @@ -1366,9 +1324,7 @@ workflows: - contracts-bedrock-checks: requires: - contracts-bedrock-build - - contracts-bedrock-validate-spacers: - requires: - - contracts-bedrock-build + - diff-asterisc-bytecode - semgrep-scan: name: semgrep-scan-local scan_command: semgrep scan --timeout=100 --config .semgrep/rules/ --error . @@ -1398,33 +1354,31 @@ workflows: on_changes: op-e2e,packages/contracts-bedrock/src uses_artifacts: true requires: ["contracts-bedrock-build"] - - go-test: - name: go-test-all - requires: - - contracts-bedrock-build - go-test-kurtosis: name: op-deployer-integration module: op-deployer test_directory: ./pkg/deployer/integration_test uses_artifacts: true requires: ["contracts-bedrock-build"] - - go-e2e-test: - name: op-e2e-HTTP-tests - module: op-e2e - target: test-http - requires: - - contracts-bedrock-build - - go-e2e-test: - name: op-e2e-action-tests - module: op-e2e - target: test-actions - requires: - - contracts-bedrock-build - - go-e2e-test: - name: op-e2e-fault-proof-tests - module: op-e2e - target: test-fault-proofs - skip_slow_tests: true + - go-tests: + packages: | + op-batcher + op-chain-ops + op-node + op-proposer + op-challenger + op-dispute-mon + op-conductor + op-program + op-service + op-supervisor + op-deployer + op-e2e/system + op-e2e/e2eutils + op-e2e/opgeth + op-e2e/interop + op-e2e/actions + op-e2e/faultproofs requires: - contracts-bedrock-build - cannon-prestate @@ -1441,16 +1395,13 @@ workflows: - go-mod-download - op-deployer-integration - op-program-compat - - op-e2e-HTTP-tests - - op-e2e-fault-proof-tests - - op-e2e-action-tests # Not needed for the devnet but we want to make sure they build successfully - cannon-docker-build - op-dispute-mon-docker-build - op-program-docker-build - op-supervisor-docker-build - proofs-tools-docker-build - - go-test-all + - go-tests - docker-build: name: <>-docker-build docker_tags: <>,<> @@ -1644,16 +1595,17 @@ workflows: skip_pattern: test context: - slack - - go-e2e-test: + - go-tests: name: op-e2e-cannon-tests - module: op-e2e - target: test-cannon notify: true mentions: "@proofs-team" + no_output_timeout: 60m + test_timeout: 59m resource_class: ethereum-optimism/latitude-fps-1 - requires: - - contracts-bedrock-build - - cannon-prestate + environment_overrides: | + export OP_E2E_CANNON_ENABLED="true" + packages: | + op-e2e/faultproofs context: - slack @@ -1742,17 +1694,6 @@ workflows: - oplabs-gcr - slack - scheduled-diff-asterisc-bytecode: - when: - or: - - equal: [build_daily, <>] - # Trigger on manual triggers if explicitly requested - - equal: [true, <>] - jobs: - - diff-asterisc-bytecode: - context: - - slack - scheduled-preimage-reproducibility: when: or: diff --git a/Makefile b/Makefile index 9830a3060cb..4c901d2158a 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# provide JUSTFLAGS for just-backed targets +include ./just/flags.mk + COMPOSEFLAGS=-d ITESTS_L2_HOST=http://localhost:9545 BEDROCK_TAGS_REMOTE?=origin @@ -94,7 +97,7 @@ submodules: ## Updates git submodules op-node: ## Builds op-node binary - make -C ./op-node op-node + just $(JUSTFLAGS) ./op-node/op-node .PHONY: op-node generate-mocks-op-node: ## Generates mocks for op-node @@ -106,11 +109,11 @@ generate-mocks-op-service: ## Generates mocks for op-service .PHONY: generate-mocks-op-service op-batcher: ## Builds op-batcher binary - make -C ./op-batcher op-batcher + just $(JUSTFLAGS) ./op-batcher/op-batcher .PHONY: op-batcher op-proposer: ## Builds op-proposer binary - make -C ./op-proposer op-proposer + just $(JUSTFLAGS) ./op-proposer/op-proposer .PHONY: op-proposer op-challenger: ## Builds op-challenger binary @@ -173,9 +176,6 @@ nuke: clean devnet-clean ## Completely clean the project directory ## Prepares for running a local devnet pre-devnet: submodules $(DEVNET_CANNON_PRESTATE_FILES) - @if ! [ -x "$$(command -v geth)" ]; then \ - make install-geth; \ - fi @if ! [ -x "$$(command -v eth2-testnet-genesis)" ]; then \ make install-eth2-testnet-genesis; \ fi @@ -247,14 +247,6 @@ update-op-geth: ## Updates the Geth version used in the project ./ops/scripts/update-op-geth.py .PHONY: update-op-geth -install-geth: ## Installs or updates Geth if versions do not match - ./ops/scripts/geth-version-checker.sh && \ - (echo "Geth versions match, not installing geth..."; true) || \ - (echo "Versions do not match, installing geth!"; \ - go install -v github.com/ethereum/go-ethereum/cmd/geth@$(shell jq -r .geth < versions.json); \ - echo "Installed geth!"; true) -.PHONY: install-geth - install-eth2-testnet-genesis: go install -v github.com/protolambda/eth2-testnet-genesis@$(shell jq -r .eth2_testnet_genesis < versions.json) .PHONY: install-eth2-testnet-genesis diff --git a/cannon/mipsevm/multithreaded/instrumented_test.go b/cannon/mipsevm/multithreaded/instrumented_test.go index 6add2cc020a..659a5b7a5c2 100644 --- a/cannon/mipsevm/multithreaded/instrumented_test.go +++ b/cannon/mipsevm/multithreaded/instrumented_test.go @@ -157,7 +157,7 @@ func TestInstrumentedState_MultithreadedProgram(t *testing.T) { "Map test passed", }, programName: "mt-map", - steps: 100_000_000, + steps: 150_000_000, }, { name: "pool test", diff --git a/go.mod b/go.mod index 5454732a74a..7e6f8737d80 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v1.0.0 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 - github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6 + github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241126105717-d31591e83048 github.com/ethereum/go-ethereum v1.14.11 github.com/fsnotify/fsnotify v1.8.0 github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb @@ -34,17 +34,17 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.12.0 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/mattn/go-isatty v0.0.20 - github.com/minio/minio-go/v7 v7.0.80 + github.com/minio/minio-go/v7 v7.0.81 github.com/multiformats/go-base32 v0.1.0 github.com/multiformats/go-multiaddr v0.14.0 - github.com/multiformats/go-multiaddr-dns v0.4.0 + github.com/multiformats/go-multiaddr-dns v0.4.1 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416 github.com/olekukonko/tablewriter v0.0.5 github.com/pkg/errors v0.9.1 github.com/pkg/profile v1.7.0 github.com/prometheus/client_golang v1.20.5 github.com/protolambda/ctxlock v0.1.0 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 github.com/urfave/cli/v2 v2.27.5 golang.org/x/crypto v0.28.0 golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c @@ -250,7 +250,7 @@ require ( rsc.io/tmplfunc v0.0.3 // indirect ) -replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.2-rc.2 +replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101411.3-rc.1.0.20241126165630-b84907bf4d95 //replace github.com/ethereum/go-ethereum => ../go-ethereum diff --git a/go.sum b/go.sum index 60b90554c2e..d18aff8414a 100644 --- a/go.sum +++ b/go.sum @@ -187,10 +187,10 @@ github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/u github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= -github.com/ethereum-optimism/op-geth v1.101411.2-rc.2 h1:3suWTU9DwBdY8Yy/ZgZLB/yBy3TwpntpkUn61mZgNpY= -github.com/ethereum-optimism/op-geth v1.101411.2-rc.2/go.mod h1:dITJzx1KXsV2KusscsktidEb00blTSyFhalq8CjfsUY= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6 h1:+AIYWDX7FeWRLnBVqPiwireTacLLGGww1slGyv+YN0o= -github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241119111730-bee358f6d6e6/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= +github.com/ethereum-optimism/op-geth v1.101411.3-rc.1.0.20241126165630-b84907bf4d95 h1:4bgr/Y/Vl2lntFvFM6l/W6P8EWLMxAdF5CRnJStHGGI= +github.com/ethereum-optimism/op-geth v1.101411.3-rc.1.0.20241126165630-b84907bf4d95/go.mod h1:zBADVb3+aon0Idb3uEg/1TFpep+Jdkz3ge9SLFDBXOo= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241126105717-d31591e83048 h1:kb220NeqVRRt/XP5JHt3i4zpLsYNCdWMM/0tDnOFk3o= +github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20241126105717-d31591e83048/go.mod h1:9feO8jcL5OZ1tvRjEfNAHz4Aggvd6373l+ZxmZZAyZs= github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA= github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= github.com/ethereum/go-verkle v0.1.1-0.20240829091221-dffa7562dbe9 h1:8NfxH2iXvJ60YRB8ChToFTUzl8awsc3cJ8CbLjGIl/A= @@ -524,8 +524,8 @@ github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4S github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk= -github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= +github.com/minio/minio-go/v7 v7.0.81 h1:SzhMN0TQ6T/xSBu6Nvw3M5M8voM+Ht8RH3hE8S7zxaA= +github.com/minio/minio-go/v7 v7.0.81/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0= github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= @@ -551,8 +551,8 @@ github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU= github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4= -github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU= -github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= +github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= @@ -778,8 +778,9 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/supranational/blst v0.3.13 h1:AYeSxdOMacwu7FBmpfloBz5pbFXDmJL33RuwnKtmTjk= github.com/supranational/blst v0.3.13/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= diff --git a/just/default.just b/just/default.just index 220c27dca5c..92503b23ba5 100644 --- a/just/default.just +++ b/just/default.just @@ -1,5 +1,6 @@ set shell := ["bash", "-c"] -PARALLEL := num_cpus() +PARALLEL_JOBS := num_cpus() -MAP_JUST := "/usr/bin/env -S parallel --shebang --jobs " + PARALLEL + " --colsep ' ' -r " + just_executable() +# TODO: this fails in CI for some reason +MAP_JUST := "/usr/bin/env -S parallel --shebang --jobs " + PARALLEL_JOBS + " --colsep ' ' -r " + just_executable() diff --git a/just/deprecated.mk b/just/deprecated.mk index a18d665c054..655f901348d 100644 --- a/just/deprecated.mk +++ b/just/deprecated.mk @@ -1,42 +1,24 @@ ifeq (, $(shell which tput)) # CI environment typically does not support tput. banner-style = $1 +else ifeq (, $(TERM)) + # Terminal type not set, so tput would fail. + banner-style = $1 else # print in bold red to bring attention. banner-style = $(shell tput bold)$(shell tput setaf 1)$1$(shell tput sgr0) endif -# Variable assignments can affect the semantic of the make targets. -# Typical use-case: setting VERSION in a release build, since CI -# doesn't preserve the git environment. -# -# We need to translate: -# "make target VAR=val" to "just VAR=val target" -# -# MAKEFLAGS is a string of the form: -# "abc --foo --bar=baz -- VAR1=val1 VAR2=val2", namely: -# - abc is the concatnation of all short flags -# - --foo and --bar=baz are long options, -# - -- is the separator between flags and variable assignments, -# - VAR1=val1 and VAR2=val2 are variable assignments -# -# Goal: ignore all CLI flags, keep only variable assignments. -# -# First remove the short flags at the beginning, or the first long-flag, -# or if there is no flag at all, the -- separator (which then makes the -# next step a noop). If there's no flag and no variable assignment, the -# result is empty anyway, so the wordlist call is safe (everything is a noop). -tmp-flags = $(wordlist 2,$(words $(MAKEFLAGS)),$(MAKEFLAGS)) -# Then remove all long options, including the -- separator, if needed. That -# leaves only variable assignments. -just-flags = $(patsubst --%,,$(tmp-flags)) +SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +include $(SELF_DIR)/flags.mk define make-deprecated-target $1: @echo - @printf %s\\n '$(call banner-style,"make $1 $(just-flags)" is deprecated. Please use "just $(just-flags) $1" instead.)' + @printf %s\\n '$(call banner-style,Deprecated make call: make $1 $(JUSTFLAGS))' + @printf %s\\n '$(call banner-style,Consider using just instead: just $(JUSTFLAGS) $1)' @echo - just $(just-flags) $1 + just $(JUSTFLAGS) $1 endef $(foreach element,$(DEPRECATED_TARGETS),$(eval $(call make-deprecated-target,$(element)))) diff --git a/just/flags.mk b/just/flags.mk new file mode 100644 index 00000000000..121a3eb70e9 --- /dev/null +++ b/just/flags.mk @@ -0,0 +1,24 @@ +# Variable assignments can affect the semantic of the make targets. +# Typical use-case: setting VERSION in a release build, since CI +# doesn't preserve the git environment. +# +# We need to translate: +# "make target VAR=val" to "just VAR=val target" +# +# MAKEFLAGS is a string of the form: +# "abc --foo --bar=baz -- VAR1=val1 VAR2=val2", namely: +# - abc is the concatnation of all short flags +# - --foo and --bar=baz are long options, +# - -- is the separator between flags and variable assignments, +# - VAR1=val1 and VAR2=val2 are variable assignments +# +# Goal: ignore all CLI flags, keep only variable assignments. +# +# First remove the short flags at the beginning, or the first long-flag, +# or if there is no flag at all, the -- separator (which then makes the +# next step a noop). If there's no flag and no variable assignment, the +# result is empty anyway, so the wordlist call is safe (everything is a noop). +tmp-flags := $(wordlist 2,$(words $(MAKEFLAGS)),$(MAKEFLAGS)) +# Then remove all long options, including the -- separator, if needed. That +# leaves only variable assignments. +JUSTFLAGS := $(patsubst --%,,$(tmp-flags)) \ No newline at end of file diff --git a/just/git.just b/just/git.just index 922286d7ab5..ac50e4e762d 100644 --- a/just/git.just +++ b/just/git.just @@ -24,3 +24,5 @@ VERSION := shell('if [ -z "$1" ]; then else echo $1 fi', _PREFERRED_TAG, _LAST_TAG) + +VERSION_META := "" diff --git a/just/go.just b/just/go.just index 5af76629509..3a394e3ec94 100644 --- a/just/go.just +++ b/just/go.just @@ -25,3 +25,7 @@ go_test SELECTOR *FLAGS: [private] go_fuzz FUZZ TIME='10s' PKG='': (go_test PKG _EXTRALDFLAGS "-fuzztime" TIME "-fuzz" FUZZ "-run" "NOTAREALTEST") + +[private] +go_generate SELECTOR *FLAGS: + go generate -v {{FLAGS}} {{SELECTOR}} \ No newline at end of file diff --git a/justfile b/justfile index 6438ba36197..acf6d8bc602 100644 --- a/justfile +++ b/justfile @@ -1,4 +1,5 @@ -issues: +# Checks that TODO comments have corresponding issues. +todo-checker: ./ops/scripts/todo-checker.sh # Runs semgrep on the entire monorepo. @@ -9,56 +10,94 @@ semgrep: semgrep-test: semgrep scan --test --config .semgrep/rules/ .semgrep/tests/ -lint-shellcheck: +# Runs shellcheck. +shellcheck: find . -type f -name '*.sh' -not -path '*/node_modules/*' -not -path './packages/contracts-bedrock/lib/*' -not -path './packages/contracts-bedrock/kout*/*' -exec sh -c 'echo "Checking $1"; shellcheck "$1"' _ {} \; +######################################################## +# DEPENDENCY MANAGEMENT # +######################################################## + +# Generic task for checking if a tool version is up to date. +check-tool-version tool: + #!/usr/bin/env bash + EXPECTED=$(jq -r .{{tool}} < versions.json) + ACTUAL=$(just print-{{tool}}) + if [ "$ACTUAL" = "$EXPECTED" ]; then + echo "✓ {{tool}} versions match" + else + echo "✗ {{tool}} version mismatch (expected $EXPECTED, got $ACTUAL), run 'just install-{{tool}}' to upgrade" + exit 1 + fi + +# Installs foundry install-foundry: - curl -L https://foundry.paradigm.xyz | bash && just update-foundry - -update-foundry: bash ./ops/scripts/install-foundry.sh +# Prints current foundry version. +print-foundry: + forge --version + +# Checks if installed foundry version is correct. check-foundry: bash ./ops/scripts/check-foundry.sh +# Installs correct kontrol version. install-kontrol: - curl -L https://kframework.org/install | bash && just update-kontrol + bash ./ops/scripts/install-kontrol.sh + +# Prints current kontrol version. +print-kontrol: + kontrol version -update-kontrol: - kup install kontrol --version v$(jq -r .kontrol < versions.json) +# Checks if installed kontrol version is correct. +check-kontrol: + just check-tool-version kontrol +# Installs correct abigen version. install-abigen: go install github.com/ethereum/go-ethereum/cmd/abigen@$(jq -r .abigen < versions.json) +# Prints current abigen version. print-abigen: abigen --version | sed -e 's/[^0-9]/ /g' -e 's/^ *//g' -e 's/ *$//g' -e 's/ /./g' -e 's/^/v/' +# Checks if installed abigen version is correct. check-abigen: - [[ $(just print-abigen) = $(cat versions.json | jq -r '.abigen') ]] && echo '✓ abigen versions match' || (echo '✗ abigen version mismatch. Run `just upgrade:abigen` to upgrade.' && exit 1) - -upgrade-abigen: - jq '.abigen = $v' --arg v $(just print:abigen) <<<$(cat versions.json) > versions.json + just check-tool-version abigen +# Installs correct slither version. install-slither: pip3 install slither-analyzer==$(jq -r .slither < versions.json) +# Prints current slither version. print-slither: slither --version +# Checks if installed slither version is correct. check-slither: - [[ $(just print-slither) = $(jq -r .slither < versions.json) ]] && echo '✓ slither versions match' || (echo '✗ slither version mismatch. Run `just upgrade-slither` to upgrade.' && exit 1) - -upgrade-slither: - jq '.slither = $v' --arg v $(just print-slither) <<<$(cat versions.json) > versions.json + just check-tool-version slither +# Installs correct semgrep version. install-semgrep: - pip3 install semgrep + pip3 install semgrep=="$(jq -r .semgrep < versions.json)" +# Prints current semgrep version. print-semgrep: - semgrep --version + semgrep --version | head -n 1 +# Checks if installed semgrep version is correct. check-semgrep: - [ "$(just print-semgrep)" = "$(jq -r .semgrep < versions.json)" ] && echo '✓ semgrep versions match' || (echo '✗ semgrep version mismatch. Run `just upgrade-semgrep` to upgrade.' && exit 1) + just check-tool-version semgrep -upgrade-semgrep: - pip3 install semgrep=="$(jq -r .semgrep < versions.json)" +# Installs correct go version. +install-go: + echo "error: go must be installed manually" && exit 1 + +# Prints current go version. +print-go: + go version | sed -E 's/.*go([0-9]+\.[0-9]+\.[0-9]+).*/\1/' + +# Checks if installed go version is correct. +check-go: + just check-tool-version go diff --git a/op-batcher/batcher/channel_config.go b/op-batcher/batcher/channel_config.go index 45dc1d4dcfa..e62ea26eee4 100644 --- a/op-batcher/batcher/channel_config.go +++ b/op-batcher/batcher/channel_config.go @@ -104,7 +104,7 @@ func (cc *ChannelConfig) Check() error { // The [ChannelTimeout] must be larger than the [SubSafetyMargin]. // Otherwise, new blocks would always be considered timed out. if cc.ChannelTimeout < cc.SubSafetyMargin { - return ErrInvalidChannelTimeout + return fmt.Errorf("%w: %d < %d", ErrInvalidChannelTimeout, cc.ChannelTimeout, cc.SubSafetyMargin) } // The max frame size must at least be able to accommodate the constant diff --git a/op-batcher/batcher/channel_manager.go b/op-batcher/batcher/channel_manager.go index 81ee0fb35a5..1da2def78da 100644 --- a/op-batcher/batcher/channel_manager.go +++ b/op-batcher/batcher/channel_manager.go @@ -480,6 +480,7 @@ func (s *channelManager) pruneSafeBlocks(newSafeHead eth.L2BlockRef) { if newSafeHead.Number+1 < oldestBlock.NumberU64() { // This could happen if there was an L1 reorg. + // Or if the sequencer restarted. s.log.Warn("safe head reversed, clearing channel manager state", "oldestBlock", eth.ToBlockID(oldestBlock), "newSafeBlock", newSafeHead) @@ -565,3 +566,10 @@ func (m *channelManager) CheckExpectedProgress(syncStatus eth.SyncStatus) error } return nil } + +func (m *channelManager) LastStoredBlock() eth.BlockID { + if m.blocks.Len() == 0 { + return eth.BlockID{} + } + return eth.ToBlockID(m.blocks[m.blocks.Len()-1]) +} diff --git a/op-batcher/batcher/driver.go b/op-batcher/batcher/driver.go index dde08ac8419..58e533becff 100644 --- a/op-batcher/batcher/driver.go +++ b/op-batcher/batcher/driver.go @@ -114,10 +114,6 @@ type BatchSubmitter struct { txpoolState TxPoolState txpoolBlockedBlob bool - // lastStoredBlock is the last block loaded into `state`. If it is empty it should be set to the l2 safe head. - lastStoredBlock eth.BlockID - lastL1Tip eth.L1BlockRef - state *channelManager } @@ -147,7 +143,6 @@ func (l *BatchSubmitter) StartBatchSubmitting() error { l.shutdownCtx, l.cancelShutdownCtx = context.WithCancel(context.Background()) l.killCtx, l.cancelKillCtx = context.WithCancel(context.Background()) l.clearState(l.shutdownCtx) - l.lastStoredBlock = eth.BlockID{} if err := l.waitForL2Genesis(); err != nil { return fmt.Errorf("error waiting for L2 genesis: %w", err) @@ -271,13 +266,11 @@ func (l *BatchSubmitter) loadBlocksIntoState(syncStatus eth.SyncStatus, ctx cont block, err := l.loadBlockIntoState(ctx, i) if errors.Is(err, ErrReorg) { l.Log.Warn("Found L2 reorg", "block_number", i) - l.lastStoredBlock = eth.BlockID{} return err } else if err != nil { l.Log.Warn("Failed to load block into state", "err", err) return err } - l.lastStoredBlock = eth.ToBlockID(block) latestBlock = block } @@ -366,29 +359,31 @@ func (l *BatchSubmitter) getSyncStatus(ctx context.Context) (*eth.SyncStatus, er } // calculateL2BlockRangeToStore determines the range (start,end] that should be loaded into the local state. -// It also takes care of initializing some local state (i.e. will modify l.lastStoredBlock in certain conditions -// as well as garbage collecting blocks which became safe) func (l *BatchSubmitter) calculateL2BlockRangeToStore(syncStatus eth.SyncStatus) (eth.BlockID, eth.BlockID, error) { if syncStatus.HeadL1 == (eth.L1BlockRef{}) { return eth.BlockID{}, eth.BlockID{}, errors.New("empty sync status") } - - // Check last stored to see if it needs to be set on startup OR set if is lagged behind. - // It lagging implies that the op-node processed some batches that were submitted prior to the current instance of the batcher being alive. - if l.lastStoredBlock == (eth.BlockID{}) { - l.Log.Info("Starting batch-submitter work at safe-head", "safe", syncStatus.SafeL2) - l.lastStoredBlock = syncStatus.SafeL2.ID() - } else if l.lastStoredBlock.Number < syncStatus.SafeL2.Number { - l.Log.Warn("Last submitted block lagged behind L2 safe head: batch submission will continue from the safe head now", "last", l.lastStoredBlock, "safe", syncStatus.SafeL2) - l.lastStoredBlock = syncStatus.SafeL2.ID() - } - // Check if we should even attempt to load any blocks. TODO: May not need this check if syncStatus.SafeL2.Number >= syncStatus.UnsafeL2.Number { - return eth.BlockID{}, eth.BlockID{}, fmt.Errorf("L2 safe head(%d) ahead of L2 unsafe head(%d)", syncStatus.SafeL2.Number, syncStatus.UnsafeL2.Number) + return eth.BlockID{}, eth.BlockID{}, fmt.Errorf("L2 safe head(%d) >= L2 unsafe head(%d)", syncStatus.SafeL2.Number, syncStatus.UnsafeL2.Number) + } + + lastStoredBlock := l.state.LastStoredBlock() + start := lastStoredBlock + end := syncStatus.UnsafeL2.ID() + + // Check last stored block to see if it is empty or has lagged behind. + // It lagging implies that the op-node processed some batches that + // were submitted prior to the current instance of the batcher being alive. + if lastStoredBlock == (eth.BlockID{}) { + l.Log.Info("Resuming batch-submitter work at safe-head", "safe", syncStatus.SafeL2) + start = syncStatus.SafeL2.ID() + } else if lastStoredBlock.Number < syncStatus.SafeL2.Number { + l.Log.Warn("Last stored block lagged behind L2 safe head: batch submission will continue from the safe head now", "last", lastStoredBlock, "safe", syncStatus.SafeL2) + start = syncStatus.SafeL2.ID() } - return l.lastStoredBlock, syncStatus.UnsafeL2.ID(), nil + return start, end, nil } // The following things occur: @@ -557,8 +552,11 @@ func (l *BatchSubmitter) throttlingLoop(ctx context.Context) { // We'd probably hit this error right after startup, so a short shutdown duration should suffice. ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - // Always returns nil. An error is only returned to expose this function as an RPC. - _ = l.StopBatchSubmitting(ctx) + // Call StopBatchSubmitting in another goroutine to avoid deadlock. + go func() { + // Always returns nil. An error is only returned to expose this function as an RPC. + _ = l.StopBatchSubmitting(ctx) + }() return } else if err != nil { l.Log.Error("SetMaxDASize rpc failed, retrying.", "err", err) @@ -701,7 +699,7 @@ func (l *BatchSubmitter) publishTxToL1(ctx context.Context, queue *txmgr.Queue[t l.Log.Error("Failed to query L1 tip", "err", err) return err } - l.recordL1Tip(l1tip) + l.Metr.RecordLatestL1Block(l1tip) // Collect next transaction data. This pulls data out of the channel, so we need to make sure // to put it back if ever da or txmgr requests fail, by calling l.recordFailedDARequest/recordFailedTx. @@ -779,7 +777,11 @@ func (l *BatchSubmitter) publishToAltDAAndL1(txdata txData, queue *txmgr.Queue[t // So we prefer to mimic the behavior of txmgr and cancel all pending DA/txmgr requests when the batcher is stopped. comm, err := l.AltDA.SetInput(l.shutdownCtx, txdata.CallData()) if err != nil { - l.Log.Error("Failed to post input to Alt DA", "error", err) + // Don't log context cancelled events because they are expected, + // and can happen after tests complete which causes a panic. + if !errors.Is(err, context.Canceled) { + l.Log.Error("Failed to post input to Alt DA", "error", err) + } // requeue frame if we fail to post to the DA Provider so it can be retried // note: this assumes that the da server caches requests, otherwise it might lead to resubmissions of the blobs l.recordFailedDARequest(txdata.ID(), err) @@ -879,14 +881,6 @@ func (l *BatchSubmitter) handleReceipt(r txmgr.TxReceipt[txRef]) { } } -func (l *BatchSubmitter) recordL1Tip(l1tip eth.L1BlockRef) { - if l.lastL1Tip == l1tip { - return - } - l.lastL1Tip = l1tip - l.Metr.RecordLatestL1Block(l1tip) -} - func (l *BatchSubmitter) recordFailedDARequest(id txID, err error) { if err != nil { l.Log.Warn("DA request failed", logFields(id, err)...) diff --git a/op-batcher/justfile b/op-batcher/justfile index 2647debbcb4..a0c671ebd28 100644 --- a/op-batcher/justfile +++ b/op-batcher/justfile @@ -24,12 +24,13 @@ batcher_fuzz_task FUZZ TIME='10s': (go_fuzz FUZZ TIME "./batcher") # Run fuzzing tests fuzz: - #!{{MAP_JUST}} batcher_fuzz_task - FuzzChannelConfig_CheckTimeout - FuzzDurationZero - FuzzDurationTimeoutMaxChannelDuration - FuzzDurationTimeoutZeroMaxChannelDuration - FuzzChannelCloseTimeout - FuzzChannelZeroCloseTimeout - FuzzSeqWindowClose - FuzzSeqWindowZeroTimeoutClose + printf "%s\n" \ + "FuzzChannelConfig_CheckTimeout" \ + "FuzzDurationZero" \ + "FuzzDurationTimeoutMaxChannelDuration" \ + "FuzzDurationTimeoutZeroMaxChannelDuration" \ + "FuzzChannelCloseTimeout" \ + "FuzzChannelZeroCloseTimeout" \ + "FuzzSeqWindowClose" \ + "FuzzSeqWindowZeroTimeoutClose" \ + | parallel -j {{PARALLEL_JOBS}} {{just_executable()}} batcher_fuzz_task {} diff --git a/op-challenger/cmd/main_test.go b/op-challenger/cmd/main_test.go index 1de92468306..daa4e44f26b 100644 --- a/op-challenger/cmd/main_test.go +++ b/op-challenger/cmd/main_test.go @@ -177,6 +177,13 @@ func TestNetwork(t *testing.T) { t.Run("UnknownNetwork", func(t *testing.T) { verifyArgsInvalid(t, "unknown chain: not-a-network", addRequiredArgsExcept(types.TraceTypeAlphabet, "--game-factory-address", "--network=not-a-network")) }) + + t.Run("ChainIDAllowedWhenGameFactoryAddressSupplied", func(t *testing.T) { + addr := common.Address{0xbb, 0xcc, 0xdd} + cfg := configForArgs(t, addRequiredArgsExcept(types.TraceTypeAlphabet, "--game-factory-address", "--network=1234", "--game-factory-address="+addr.Hex())) + require.Equal(t, addr, cfg.GameFactoryAddress) + require.Equal(t, "1234", cfg.Cannon.Network) + }) } func TestGameAllowlist(t *testing.T) { diff --git a/op-challenger/config/config.go b/op-challenger/config/config.go index 9f4a5f5f375..6faf7c9fdca 100644 --- a/op-challenger/config/config.go +++ b/op-challenger/config/config.go @@ -6,6 +6,7 @@ import ( "net/url" "runtime" "slices" + "strconv" "time" "github.com/ethereum-optimism/optimism/op-challenger/game/fault/trace/vm" @@ -230,7 +231,10 @@ func (c Config) Check() error { return ErrCannonNetworkAndL2Genesis } if ch := chaincfg.ChainByName(c.Cannon.Network); ch == nil { - return fmt.Errorf("%w: %v", ErrCannonNetworkUnknown, c.Cannon.Network) + // Check if this looks like a chain ID that could be a custom chain configuration. + if _, err := strconv.ParseUint(c.Cannon.Network, 10, 32); err != nil { + return fmt.Errorf("%w: %v", ErrCannonNetworkUnknown, c.Cannon.Network) + } } } if c.CannonAbsolutePreState == "" && c.CannonAbsolutePreStateBaseURL == nil { diff --git a/op-challenger/config/config_test.go b/op-challenger/config/config_test.go index 0d922b77fe9..bf910ab83e8 100644 --- a/op-challenger/config/config_test.go +++ b/op-challenger/config/config_test.go @@ -232,6 +232,18 @@ func TestCannonRequiredArgs(t *testing.T) { require.ErrorIs(t, cfg.Check(), ErrCannonNetworkUnknown) }) + t.Run(fmt.Sprintf("TestNetworkMayBeAnyChainID-%v", traceType), func(t *testing.T) { + cfg := validConfig(traceType) + cfg.Cannon.Network = "467294" + require.NoError(t, cfg.Check()) + }) + + t.Run(fmt.Sprintf("TestNetworkInvalidWhenNotEntirelyNumeric-%v", traceType), func(t *testing.T) { + cfg := validConfig(traceType) + cfg.Cannon.Network = "467294a" + require.ErrorIs(t, cfg.Check(), ErrCannonNetworkUnknown) + }) + t.Run(fmt.Sprintf("TestDebugInfoEnabled-%v", traceType), func(t *testing.T) { cfg := validConfig(traceType) require.True(t, cfg.Cannon.DebugInfo) diff --git a/op-challenger/game/fault/trace/vm/kona_server_executor.go b/op-challenger/game/fault/trace/vm/kona_server_executor.go index baaa253088f..ffb74d9a31b 100644 --- a/op-challenger/game/fault/trace/vm/kona_server_executor.go +++ b/op-challenger/game/fault/trace/vm/kona_server_executor.go @@ -9,8 +9,7 @@ import ( ) type KonaExecutor struct { - nativeMode bool - clientBinPath string + nativeMode bool } var _ OracleServerExecutor = (*KonaExecutor)(nil) @@ -19,8 +18,8 @@ func NewKonaExecutor() *KonaExecutor { return &KonaExecutor{nativeMode: false} } -func NewNativeKonaExecutor(clientBinPath string) *KonaExecutor { - return &KonaExecutor{nativeMode: true, clientBinPath: clientBinPath} +func NewNativeKonaExecutor() *KonaExecutor { + return &KonaExecutor{nativeMode: true} } func (s *KonaExecutor) OracleCommand(cfg Config, dataDir string, inputs utils.LocalGameInputs) ([]string, error) { @@ -37,7 +36,7 @@ func (s *KonaExecutor) OracleCommand(cfg Config, dataDir string, inputs utils.Lo } if s.nativeMode { - args = append(args, "--exec", s.clientBinPath) + args = append(args, "--native") } else { args = append(args, "--server") args = append(args, "--data-dir", dataDir) diff --git a/op-challenger/sender/sender_test.go b/op-challenger/sender/sender_test.go index 5169ae67192..1d4e17047b7 100644 --- a/op-challenger/sender/sender_test.go +++ b/op-challenger/sender/sender_test.go @@ -129,8 +129,14 @@ func (s *stubTxMgr) Send(ctx context.Context, candidate txmgr.TxCandidate) (*typ return <-ch, nil } +// SendAsync simply wraps Send to make it non blocking. It does not guarantee transaction nonce ordering, +// unlike the production txMgr. func (s *stubTxMgr) SendAsync(ctx context.Context, candidate txmgr.TxCandidate, ch chan txmgr.SendResponse) { - panic("unimplemented") + go func() { + receipt, err := s.Send(ctx, candidate) + resp := txmgr.SendResponse{Receipt: receipt, Err: err} + ch <- resp + }() } func (s *stubTxMgr) recordTx(candidate txmgr.TxCandidate) chan *types.Receipt { diff --git a/op-conductor/consensus/iface.go b/op-conductor/consensus/iface.go index e0dcb6efd5a..2de955c1201 100644 --- a/op-conductor/consensus/iface.go +++ b/op-conductor/consensus/iface.go @@ -72,9 +72,9 @@ type Consensus interface { // ClusterMembership returns the current cluster membership configuration and associated version. ClusterMembership() (*ClusterMembership, error) - // CommitPayload commits latest unsafe payload to the FSM in a strongly consistent fashion. + // CommitUnsafePayload commits latest unsafe payload to the FSM in a strongly consistent fashion. CommitUnsafePayload(payload *eth.ExecutionPayloadEnvelope) error - // LatestUnsafeBlock returns the latest unsafe payload from FSM in a strongly consistent fashion. + // LatestUnsafePayload returns the latest unsafe payload from FSM in a strongly consistent fashion. LatestUnsafePayload() (*eth.ExecutionPayloadEnvelope, error) // Shutdown shuts down the consensus protocol client. diff --git a/op-deployer/pkg/deployer/apply.go b/op-deployer/pkg/deployer/apply.go index f02716c655a..b4ff343de35 100644 --- a/op-deployer/pkg/deployer/apply.go +++ b/op-deployer/pkg/deployer/apply.go @@ -141,6 +141,9 @@ func ApplyPipeline( opts ApplyPipelineOpts, ) error { intent := opts.Intent + if err := intent.Check(); err != nil { + return err + } st := opts.State progressor := func(curr, total int64) { diff --git a/op-deployer/pkg/deployer/artifacts/locator.go b/op-deployer/pkg/deployer/artifacts/locator.go index aa44d43644c..42b838f66a7 100644 --- a/op-deployer/pkg/deployer/artifacts/locator.go +++ b/op-deployer/pkg/deployer/artifacts/locator.go @@ -70,11 +70,7 @@ func (a *Locator) MarshalText() ([]byte, error) { return []byte(a.URL.String()), nil } - if a.Tag != "" { - return []byte("tag://" + a.Tag), nil - } - - return nil, fmt.Errorf("no URL, path or tag set") + return []byte("tag://" + a.Tag), nil } func (a *Locator) IsTag() bool { diff --git a/op-deployer/pkg/deployer/bootstrap/asterisc.go b/op-deployer/pkg/deployer/bootstrap/asterisc.go new file mode 100644 index 00000000000..bad2b5ebbb1 --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/asterisc.go @@ -0,0 +1,185 @@ +package bootstrap + +import ( + "context" + "crypto/ecdsa" + "fmt" + "strings" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" + "github.com/ethereum-optimism/optimism/op-chain-ops/script/forking" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/opcm" + opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" + "github.com/ethereum-optimism/optimism/op-service/ctxinterrupt" + "github.com/ethereum-optimism/optimism/op-service/ioutil" + "github.com/ethereum-optimism/optimism/op-service/jsonutil" + oplog "github.com/ethereum-optimism/optimism/op-service/log" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/rpc" + "github.com/urfave/cli/v2" +) + +type AsteriscConfig struct { + L1RPCUrl string + PrivateKey string + Logger log.Logger + ArtifactsLocator *artifacts.Locator + + privateKeyECDSA *ecdsa.PrivateKey + + PreimageOracle common.Address +} + +func (c *AsteriscConfig) Check() error { + if c.L1RPCUrl == "" { + return fmt.Errorf("l1RPCUrl must be specified") + } + + if c.PrivateKey == "" { + return fmt.Errorf("private key must be specified") + } + + privECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(c.PrivateKey, "0x")) + if err != nil { + return fmt.Errorf("failed to parse private key: %w", err) + } + c.privateKeyECDSA = privECDSA + + if c.Logger == nil { + return fmt.Errorf("logger must be specified") + } + + if c.ArtifactsLocator == nil { + return fmt.Errorf("artifacts locator must be specified") + } + + if c.PreimageOracle == (common.Address{}) { + return fmt.Errorf("preimage oracle must be specified") + } + + return nil +} + +func AsteriscCLI(cliCtx *cli.Context) error { + logCfg := oplog.ReadCLIConfig(cliCtx) + l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) + oplog.SetGlobalLogHandler(l.Handler()) + + l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) + privateKey := cliCtx.String(deployer.PrivateKeyFlagName) + artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) + artifactsLocator := new(artifacts.Locator) + if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { + return fmt.Errorf("failed to parse artifacts URL: %w", err) + } + + preimageOracle := common.HexToAddress(cliCtx.String(PreimageOracleFlagName)) + + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + + return Asterisc(ctx, AsteriscConfig{ + L1RPCUrl: l1RPCUrl, + PrivateKey: privateKey, + Logger: l, + ArtifactsLocator: artifactsLocator, + PreimageOracle: preimageOracle, + }) +} + +func Asterisc(ctx context.Context, cfg AsteriscConfig) error { + if err := cfg.Check(); err != nil { + return fmt.Errorf("invalid config for Asterisc: %w", err) + } + + lgr := cfg.Logger + progressor := func(curr, total int64) { + lgr.Info("artifacts download progress", "current", curr, "total", total) + } + + artifactsFS, cleanup, err := artifacts.Download(ctx, cfg.ArtifactsLocator, progressor) + if err != nil { + return fmt.Errorf("failed to download artifacts: %w", err) + } + defer func() { + if err := cleanup(); err != nil { + lgr.Warn("failed to clean up artifacts", "err", err) + } + }() + + l1Client, err := ethclient.Dial(cfg.L1RPCUrl) + if err != nil { + return fmt.Errorf("failed to connect to L1 RPC: %w", err) + } + + chainID, err := l1Client.ChainID(ctx) + if err != nil { + return fmt.Errorf("failed to get chain ID: %w", err) + } + + signer := opcrypto.SignerFnFromBind(opcrypto.PrivateKeySignerFn(cfg.privateKeyECDSA, chainID)) + chainDeployer := crypto.PubkeyToAddress(cfg.privateKeyECDSA.PublicKey) + + bcaster, err := broadcaster.NewKeyedBroadcaster(broadcaster.KeyedBroadcasterOpts{ + Logger: lgr, + ChainID: chainID, + Client: l1Client, + Signer: signer, + From: chainDeployer, + }) + if err != nil { + return fmt.Errorf("failed to create broadcaster: %w", err) + } + + l1RPC, err := rpc.Dial(cfg.L1RPCUrl) + if err != nil { + return fmt.Errorf("failed to connect to L1 RPC: %w", err) + } + + l1Host, err := env.DefaultScriptHost( + bcaster, + lgr, + chainDeployer, + artifactsFS, + script.WithForkHook(func(cfg *script.ForkConfig) (forking.ForkSource, error) { + src, err := forking.RPCSourceByNumber(cfg.URLOrAlias, l1RPC, *cfg.BlockNumber) + if err != nil { + return nil, fmt.Errorf("failed to create RPC fork source: %w", err) + } + return forking.Cache(src), nil + }), + ) + if err != nil { + return fmt.Errorf("failed to create script host: %w", err) + } + + dgo, err := opcm.DeployAsterisc( + l1Host, + opcm.DeployAsteriscInput{ + PreimageOracle: cfg.PreimageOracle, + }, + ) + if err != nil { + return fmt.Errorf("error deploying asterisc VM: %w", err) + } + + if _, err := bcaster.Broadcast(ctx); err != nil { + return fmt.Errorf("failed to broadcast: %w", err) + } + + lgr.Info("deployed asterisc VM") + + if err := jsonutil.WriteJSON(dgo, ioutil.ToStdOut()); err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + return nil +} diff --git a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go index 451b0741f24..67b1b66760a 100644 --- a/op-deployer/pkg/deployer/bootstrap/delayed_weth.go +++ b/op-deployer/pkg/deployer/bootstrap/delayed_weth.go @@ -69,22 +69,31 @@ func DelayedWETHCLI(cliCtx *cli.Context) error { l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) oplog.SetGlobalLogHandler(l.Handler()) + config, err := NewDelayedWETHConfigFromClI(cliCtx, l) + if err != nil { + return err + } + + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + + return DelayedWETH(ctx, config) +} + +func NewDelayedWETHConfigFromClI(cliCtx *cli.Context, l log.Logger) (DelayedWETHConfig, error) { l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) privateKey := cliCtx.String(deployer.PrivateKeyFlagName) artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) artifactsLocator := new(artifacts2.Locator) if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { - return fmt.Errorf("failed to parse artifacts URL: %w", err) + return DelayedWETHConfig{}, fmt.Errorf("failed to parse artifacts URL: %w", err) } - - ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) - - return DelayedWETH(ctx, DelayedWETHConfig{ + config := DelayedWETHConfig{ L1RPCUrl: l1RPCUrl, PrivateKey: privateKey, Logger: l, ArtifactsLocator: artifactsLocator, - }) + } + return config, nil } func DelayedWETH(ctx context.Context, cfg DelayedWETHConfig) error { diff --git a/op-deployer/pkg/deployer/bootstrap/delayed_weth_test.go b/op-deployer/pkg/deployer/bootstrap/delayed_weth_test.go new file mode 100644 index 00000000000..5e3b667b9b2 --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/delayed_weth_test.go @@ -0,0 +1,45 @@ +package bootstrap + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-service/cliapp" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" + "github.com/urfave/cli/v2" +) + +func TestNewDelayedWETHConfigFromCLI(t *testing.T) { + ctx, err := parseCLIArgs(DelayedWETHFlags, + "--artifacts-locator", "tag://op-contracts/v1.6.0", + "--l1-rpc-url", "http://foo", + "--private-key", "0x123456") + require.NoError(t, err) + + logger := testlog.Logger(t, log.LvlInfo) + cfg, err := NewDelayedWETHConfigFromClI(ctx, logger) + require.NoError(t, err) + require.Same(t, logger, cfg.Logger) + require.Equal(t, "op-contracts/v1.6.0", cfg.ArtifactsLocator.Tag) + require.True(t, cfg.ArtifactsLocator.IsTag()) + require.Equal(t, "0x123456", cfg.PrivateKey) +} + +func parseCLIArgs(flags []cli.Flag, args ...string) (*cli.Context, error) { + app := cli.NewApp() + app.Flags = cliapp.ProtectFlags(flags) + var ctx *cli.Context + app.Action = func(c *cli.Context) error { + ctx = c + return nil + } + argsWithCmd := make([]string, len(args)+1) + argsWithCmd[0] = "bootstrap" + copy(argsWithCmd[1:], args) + err := app.Run(argsWithCmd) + if err != nil { + return nil, err + } + return ctx, nil +} diff --git a/op-deployer/pkg/deployer/bootstrap/dispute_game.go b/op-deployer/pkg/deployer/bootstrap/dispute_game.go index d3efe36f9ca..f33f5106053 100644 --- a/op-deployer/pkg/deployer/bootstrap/dispute_game.go +++ b/op-deployer/pkg/deployer/bootstrap/dispute_game.go @@ -7,6 +7,9 @@ import ( "strings" artifacts2 "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" + "github.com/ethereum-optimism/optimism/packages/contracts-bedrock/snapshots" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" @@ -36,9 +39,7 @@ type DisputeGameConfig struct { privateKeyECDSA *ecdsa.PrivateKey - MinProposalSizeBytes uint64 - ChallengePeriodSeconds uint64 - MipsVersion uint64 + Vm common.Address GameKind string GameType uint32 AbsolutePrestate common.Hash @@ -84,22 +85,44 @@ func DisputeGameCLI(cliCtx *cli.Context) error { l := oplog.NewLogger(oplog.AppOut(cliCtx), logCfg) oplog.SetGlobalLogHandler(l.Handler()) + cfg, err := NewDisputeGameConfigFromCLI(cliCtx, l) + if err != nil { + return err + } + ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) + return DisputeGame(ctx, cfg) +} + +func NewDisputeGameConfigFromCLI(cliCtx *cli.Context, l log.Logger) (DisputeGameConfig, error) { l1RPCUrl := cliCtx.String(deployer.L1RPCURLFlagName) privateKey := cliCtx.String(deployer.PrivateKeyFlagName) artifactsURLStr := cliCtx.String(ArtifactsLocatorFlagName) artifactsLocator := new(artifacts2.Locator) if err := artifactsLocator.UnmarshalText([]byte(artifactsURLStr)); err != nil { - return fmt.Errorf("failed to parse artifacts URL: %w", err) + return DisputeGameConfig{}, fmt.Errorf("failed to parse artifacts URL: %w", err) } - ctx := ctxinterrupt.WithCancelOnInterrupt(cliCtx.Context) - - return DisputeGame(ctx, DisputeGameConfig{ + cfg := DisputeGameConfig{ L1RPCUrl: l1RPCUrl, PrivateKey: privateKey, Logger: l, ArtifactsLocator: artifactsLocator, - }) + + Vm: common.HexToAddress(cliCtx.String(VmFlagName)), + GameKind: cliCtx.String(GameKindFlagName), + GameType: uint32(cliCtx.Uint64(GameTypeFlagName)), + AbsolutePrestate: common.HexToHash(cliCtx.String(AbsolutePrestateFlagName)), + MaxGameDepth: cliCtx.Uint64(MaxGameDepthFlagName), + SplitDepth: cliCtx.Uint64(SplitDepthFlagName), + ClockExtension: cliCtx.Uint64(ClockExtensionFlagName), + MaxClockDuration: cliCtx.Uint64(MaxClockDurationFlagName), + DelayedWethProxy: common.HexToAddress(cliCtx.String(DelayedWethProxyFlagName)), + AnchorStateRegistryProxy: common.HexToAddress(cliCtx.String(AnchorStateRegistryProxyFlagName)), + L2ChainId: cliCtx.Uint64(L2ChainIdFlagName), + Proposer: common.HexToAddress(cliCtx.String(ProposerFlagName)), + Challenger: common.HexToAddress(cliCtx.String(ChallengerFlagName)), + } + return cfg, nil } func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { @@ -175,6 +198,25 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { release = "dev" } + // We need to etch the VM and PreimageOracle addresses so that they have nonzero code + // and the checks in the FaultDisputeGame constructor pass. + oracleAddr, err := loadOracleAddr(ctx, l1Client, cfg.Vm) + if err != nil { + return err + } + addresses := []common.Address{ + cfg.Vm, + oracleAddr, + } + for _, addr := range addresses { + code, err := l1Client.CodeAt(ctx, addr, nil) + if err != nil { + return fmt.Errorf("failed to get code for %v: %w", addr, err) + } + host.ImportAccount(addr, types.Account{ + Code: code, + }) + } lgr.Info("deploying dispute game", "release", release) dgo, err := opcm.DeployDisputeGame( @@ -182,9 +224,7 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { opcm.DeployDisputeGameInput{ Release: release, StandardVersionsToml: standardVersionsTOML, - MipsVersion: cfg.MipsVersion, - MinProposalSizeBytes: cfg.MinProposalSizeBytes, - ChallengePeriodSeconds: cfg.ChallengePeriodSeconds, + VmAddress: cfg.Vm, GameKind: cfg.GameKind, GameType: cfg.GameType, AbsolutePrestate: cfg.AbsolutePrestate, @@ -214,3 +254,15 @@ func DisputeGame(ctx context.Context, cfg DisputeGameConfig) error { } return nil } + +func loadOracleAddr(ctx context.Context, l1Client *ethclient.Client, vmAddr common.Address) (common.Address, error) { + callData, err := snapshots.LoadMIPSABI().Pack("oracle") + if err != nil { + return common.Address{}, fmt.Errorf("failed to create vm.oracle() calldata: %w", err) + } + result, err := l1Client.CallContract(ctx, ethereum.CallMsg{Data: callData, To: &vmAddr}, nil) + if err != nil { + return common.Address{}, fmt.Errorf("failed to call vm.oracle(): %w", err) + } + return common.BytesToAddress(result), nil +} diff --git a/op-deployer/pkg/deployer/bootstrap/dispute_game_test.go b/op-deployer/pkg/deployer/bootstrap/dispute_game_test.go new file mode 100644 index 00000000000..e24ae3754df --- /dev/null +++ b/op-deployer/pkg/deployer/bootstrap/dispute_game_test.go @@ -0,0 +1,66 @@ +package bootstrap + +import ( + "reflect" + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/standard" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestNewDisputeGameConfigFromCLI(t *testing.T) { + ctx, err := parseCLIArgs(DisputeGameFlags, + "--artifacts-locator", "tag://op-contracts/v1.6.0", + "--l1-rpc-url", "http://foo", + "--private-key", "0x123456", + + "--game-type", "2", + "--delayed-weth-proxy", common.Address{0xaa}.Hex(), + "--anchor-state-registry-proxy", common.Address{0xbb}.Hex(), + "--l2-chain-id", "901", + "--proposer", common.Address{0xcc}.Hex(), + "--challenger", common.Address{0xdd}.Hex(), + "--vm", common.Address{0xee}.Hex(), + ) + require.NoError(t, err) + + logger := testlog.Logger(t, log.LvlInfo) + cfg, err := NewDisputeGameConfigFromCLI(ctx, logger) + require.NoError(t, err) + require.Same(t, logger, cfg.Logger) + require.Equal(t, "op-contracts/v1.6.0", cfg.ArtifactsLocator.Tag) + require.True(t, cfg.ArtifactsLocator.IsTag()) + require.Equal(t, "0x123456", cfg.PrivateKey) + require.Equal(t, "FaultDisputeGame", cfg.GameKind) + require.Equal(t, uint32(2), cfg.GameType) + require.Equal(t, standard.DisputeAbsolutePrestate, cfg.AbsolutePrestate) + require.Equal(t, standard.DisputeMaxGameDepth, cfg.MaxGameDepth) + require.Equal(t, standard.DisputeSplitDepth, cfg.SplitDepth) + require.Equal(t, standard.DisputeClockExtension, cfg.ClockExtension) + require.Equal(t, standard.DisputeMaxClockDuration, cfg.MaxClockDuration) + require.Equal(t, common.Address{0xaa}, cfg.DelayedWethProxy) + require.Equal(t, common.Address{0xbb}, cfg.AnchorStateRegistryProxy) + require.Equal(t, common.Address{0xcc}, cfg.Proposer) + require.Equal(t, common.Address{0xdd}, cfg.Challenger) + require.Equal(t, common.Address{0xee}, cfg.Vm) + require.Equal(t, uint64(901), cfg.L2ChainId) + + // Check all fields are set to ensure any newly added fields don't get missed. + cfgRef := reflect.ValueOf(cfg) + cfgType := reflect.TypeOf(cfg) + var unsetFields []string + for i := 0; i < cfgRef.NumField(); i++ { + field := cfgType.Field(i) + if field.Type == reflect.TypeOf(cfg.privateKeyECDSA) { + // privateKeyECDSA is only set when Check() is called so skip it. + continue + } + if cfgRef.Field(i).IsZero() { + unsetFields = append(unsetFields, field.Name) + } + } + require.Empty(t, unsetFields, "Found unset fields in config") +} diff --git a/op-deployer/pkg/deployer/bootstrap/flags.go b/op-deployer/pkg/deployer/bootstrap/flags.go index 73f99e7b432..58fb7fc1436 100644 --- a/op-deployer/pkg/deployer/bootstrap/flags.go +++ b/op-deployer/pkg/deployer/bootstrap/flags.go @@ -16,6 +16,7 @@ const ( ProofMaturityDelaySecondsFlagName = "proof-maturity-delay-seconds" DisputeGameFinalityDelaySecondsFlagName = "dispute-game-finality-delay-seconds" MIPSVersionFlagName = "mips-version" + VmFlagName = "vm" GameKindFlagName = "game-kind" GameTypeFlagName = "game-type" AbsolutePrestateFlagName = "absolute-prestate" @@ -73,6 +74,11 @@ var ( EnvVars: deployer.PrefixEnvVar("MIPS_VERSION"), Value: standard.MIPSVersion, } + VmFlag = &cli.StringFlag{ + Name: VmFlagName, + Usage: "VM contract address.", + EnvVars: deployer.PrefixEnvVar("VM"), + } GameKindFlag = &cli.StringFlag{ Name: GameKindFlagName, Usage: "Game kind (FaultDisputeGame or PermissionedDisputeGame).", @@ -173,7 +179,7 @@ var DisputeGameFlags = []cli.Flag{ ArtifactsLocatorFlag, MinProposalSizeBytesFlag, ChallengePeriodSecondsFlag, - MIPSVersionFlag, + VmFlag, GameKindFlag, GameTypeFlag, AbsolutePrestateFlag, @@ -188,14 +194,17 @@ var DisputeGameFlags = []cli.Flag{ ChallengerFlag, } -var MIPSFlags = []cli.Flag{ +var BaseFPVMFlags = []cli.Flag{ deployer.L1RPCURLFlag, deployer.PrivateKeyFlag, ArtifactsLocatorFlag, PreimageOracleFlag, - MIPSVersionFlag, } +var MIPSFlags = append(BaseFPVMFlags, MIPSVersionFlag) + +var AsteriscFlags = BaseFPVMFlags + var Commands = []*cli.Command{ { Name: "opcm", @@ -221,4 +230,10 @@ var Commands = []*cli.Command{ Flags: cliapp.ProtectFlags(MIPSFlags), Action: MIPSCLI, }, + { + Name: "asterisc", + Usage: "Bootstrap an instance of Asterisc.", + Flags: cliapp.ProtectFlags(AsteriscFlags), + Action: AsteriscCLI, + }, } diff --git a/op-deployer/pkg/deployer/flags.go b/op-deployer/pkg/deployer/flags.go index 2611957f0ad..a58d35bbbb8 100644 --- a/op-deployer/pkg/deployer/flags.go +++ b/op-deployer/pkg/deployer/flags.go @@ -20,6 +20,7 @@ const ( OutdirFlagName = "outdir" PrivateKeyFlagName = "private-key" DeploymentStrategyFlagName = "deployment-strategy" + IntentConfigTypeFlagName = "intent-config-type" ) var ( @@ -35,7 +36,7 @@ var ( Name: L1ChainIDFlagName, Usage: "Chain ID of the L1 chain.", EnvVars: PrefixEnvVar("L1_CHAIN_ID"), - Value: 900, + Value: 11155111, } L2ChainIDsFlag = &cli.StringFlag{ Name: L2ChainIDsFlagName, @@ -62,6 +63,17 @@ var ( EnvVars: PrefixEnvVar("DEPLOYMENT_STRATEGY"), Value: string(state.DeploymentStrategyLive), } + IntentConfigTypeFlag = &cli.StringFlag{ + Name: IntentConfigTypeFlagName, + Usage: fmt.Sprintf("Intent config type to use. Options: %s (default), %s, %s, %s, %s", + state.IntentConfigTypeStandard, + state.IntentConfigTypeCustom, + state.IntentConfigTypeStrict, + state.IntentConfigTypeStandardOverrides, + state.IntentConfigTypeStrictOverrides), + EnvVars: PrefixEnvVar("INTENT_CONFIG_TYPE"), + Value: string(state.IntentConfigTypeStandard), + } ) var GlobalFlags = append([]cli.Flag{}, oplog.CLIFlags(EnvVarPrefix)...) @@ -71,6 +83,7 @@ var InitFlags = []cli.Flag{ L2ChainIDsFlag, WorkdirFlag, DeploymentStrategyFlag, + IntentConfigTypeFlag, } var ApplyFlags = []cli.Flag{ diff --git a/op-deployer/pkg/deployer/init.go b/op-deployer/pkg/deployer/init.go index 3a8ae27d2ae..deca136558c 100644 --- a/op-deployer/pkg/deployer/init.go +++ b/op-deployer/pkg/deployer/init.go @@ -7,19 +7,17 @@ import ( "path" "strings" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" - "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/state" op_service "github.com/ethereum-optimism/optimism/op-service" - "github.com/ethereum-optimism/optimism/op-chain-ops/devkeys" "github.com/ethereum/go-ethereum/common" "github.com/urfave/cli/v2" ) type InitConfig struct { DeploymentStrategy state.DeploymentStrategy + IntentConfigType state.IntentConfigType L1ChainID uint64 Outdir string L2ChainIDs []common.Hash @@ -51,6 +49,7 @@ func InitCLI() func(ctx *cli.Context) error { l1ChainID := ctx.Uint64(L1ChainIDFlagName) outdir := ctx.String(OutdirFlagName) l2ChainIDsRaw := ctx.String(L2ChainIDsFlagName) + intentConfigType := ctx.String(IntentConfigTypeFlagName) if len(l2ChainIDsRaw) == 0 { return fmt.Errorf("must specify at least one L2 chain ID") @@ -68,6 +67,7 @@ func InitCLI() func(ctx *cli.Context) error { err := Init(InitConfig{ DeploymentStrategy: state.DeploymentStrategy(deploymentStrategy), + IntentConfigType: state.IntentConfigType(intentConfigType), L1ChainID: l1ChainID, Outdir: outdir, L2ChainIDs: l2ChainIDs, @@ -86,55 +86,12 @@ func Init(cfg InitConfig) error { return fmt.Errorf("invalid config for init: %w", err) } - intent := &state.Intent{ - DeploymentStrategy: cfg.DeploymentStrategy, - L1ChainID: cfg.L1ChainID, - FundDevAccounts: true, - L1ContractsLocator: artifacts.DefaultL1ContractsLocator, - L2ContractsLocator: artifacts.DefaultL2ContractsLocator, - } - - l1ChainIDBig := intent.L1ChainIDBig() - - dk, err := devkeys.NewMnemonicDevKeys(devkeys.TestMnemonic) + intent, err := state.NewIntent(cfg.IntentConfigType, cfg.DeploymentStrategy, cfg.L1ChainID, cfg.L2ChainIDs) if err != nil { - return fmt.Errorf("failed to create dev keys: %w", err) - } - - addrFor := func(key devkeys.Key) common.Address { - // The error below should never happen, so panic if it does. - addr, err := dk.Address(key) - if err != nil { - panic(err) - } - return addr - } - intent.SuperchainRoles = &state.SuperchainRoles{ - ProxyAdminOwner: addrFor(devkeys.L1ProxyAdminOwnerRole.Key(l1ChainIDBig)), - ProtocolVersionsOwner: addrFor(devkeys.SuperchainProtocolVersionsOwner.Key(l1ChainIDBig)), - Guardian: addrFor(devkeys.SuperchainConfigGuardianKey.Key(l1ChainIDBig)), - } - - for _, l2ChainID := range cfg.L2ChainIDs { - l2ChainIDBig := l2ChainID.Big() - intent.Chains = append(intent.Chains, &state.ChainIntent{ - ID: l2ChainID, - BaseFeeVaultRecipient: addrFor(devkeys.BaseFeeVaultRecipientRole.Key(l2ChainIDBig)), - L1FeeVaultRecipient: addrFor(devkeys.L1FeeVaultRecipientRole.Key(l2ChainIDBig)), - SequencerFeeVaultRecipient: addrFor(devkeys.SequencerFeeVaultRecipientRole.Key(l2ChainIDBig)), - Eip1559Denominator: 50, - Eip1559Elasticity: 6, - Roles: state.ChainRoles{ - L1ProxyAdminOwner: addrFor(devkeys.L1ProxyAdminOwnerRole.Key(l2ChainIDBig)), - L2ProxyAdminOwner: addrFor(devkeys.L2ProxyAdminOwnerRole.Key(l2ChainIDBig)), - SystemConfigOwner: addrFor(devkeys.SystemConfigOwner.Key(l2ChainIDBig)), - UnsafeBlockSigner: addrFor(devkeys.SequencerP2PRole.Key(l2ChainIDBig)), - Batcher: addrFor(devkeys.BatcherRole.Key(l2ChainIDBig)), - Proposer: addrFor(devkeys.ProposerRole.Key(l2ChainIDBig)), - Challenger: addrFor(devkeys.ChallengerRole.Key(l2ChainIDBig)), - }, - }) + return err } + intent.DeploymentStrategy = cfg.DeploymentStrategy + intent.ConfigType = cfg.IntentConfigType st := &state.State{ Version: 1, diff --git a/op-deployer/pkg/deployer/integration_test/apply_test.go b/op-deployer/pkg/deployer/integration_test/apply_test.go index f977fbbb0c7..98a5c89b851 100644 --- a/op-deployer/pkg/deployer/integration_test/apply_test.go +++ b/op-deployer/pkg/deployer/integration_test/apply_test.go @@ -16,6 +16,8 @@ import ( "testing" "time" + "github.com/ethereum-optimism/optimism/op-e2e/e2eutils/retryproxy" + altda "github.com/ethereum-optimism/optimism/op-alt-da" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/inspect" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -195,8 +197,14 @@ func testApplyExistingOPCM(t *testing.T, l1ChainID uint64, forkRPCUrl string, ve ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) defer cancel() + retryProxy := retryproxy.New(lgr, forkRPCUrl) + require.NoError(t, retryProxy.Start()) + t.Cleanup(func() { + require.NoError(t, retryProxy.Stop()) + }) + runner, err := anvil.New( - forkRPCUrl, + retryProxy.Endpoint(), lgr, ) require.NoError(t, err) @@ -706,6 +714,7 @@ func newIntent( l2Loc *artifacts.Locator, ) (*state.Intent, *state.State) { intent := &state.Intent{ + ConfigType: state.IntentConfigTypeCustom, DeploymentStrategy: state.DeploymentStrategyLive, L1ChainID: l1ChainID.Uint64(), SuperchainRoles: &state.SuperchainRoles{ @@ -732,8 +741,9 @@ func newChainIntent(t *testing.T, dk *devkeys.MnemonicDevKeys, l1ChainID *big.In BaseFeeVaultRecipient: addrFor(t, dk, devkeys.BaseFeeVaultRecipientRole.Key(l1ChainID)), L1FeeVaultRecipient: addrFor(t, dk, devkeys.L1FeeVaultRecipientRole.Key(l1ChainID)), SequencerFeeVaultRecipient: addrFor(t, dk, devkeys.SequencerFeeVaultRecipientRole.Key(l1ChainID)), - Eip1559Denominator: 50, - Eip1559Elasticity: 6, + Eip1559DenominatorCanyon: standard.Eip1559DenominatorCanyon, + Eip1559Denominator: standard.Eip1559Denominator, + Eip1559Elasticity: standard.Eip1559Elasticity, Roles: state.ChainRoles{ L1ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), L2ProxyAdminOwner: addrFor(t, dk, devkeys.L2ProxyAdminOwnerRole.Key(l1ChainID)), diff --git a/op-deployer/pkg/deployer/opcm/asterisc.go b/op-deployer/pkg/deployer/opcm/asterisc.go new file mode 100644 index 00000000000..9ba8959e354 --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/asterisc.go @@ -0,0 +1,64 @@ +package opcm + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + + "github.com/ethereum-optimism/optimism/op-chain-ops/script" +) + +type DeployAsteriscInput struct { + PreimageOracle common.Address +} + +func (input *DeployAsteriscInput) InputSet() bool { + return true +} + +type DeployAsteriscOutput struct { + AsteriscSingleton common.Address +} + +func (output *DeployAsteriscOutput) CheckOutput(input common.Address) error { + return nil +} + +type DeployAsteriscScript struct { + Run func(input, output common.Address) error +} + +func DeployAsterisc( + host *script.Host, + input DeployAsteriscInput, +) (DeployAsteriscOutput, error) { + var output DeployAsteriscOutput + inputAddr := host.NewScriptAddress() + outputAddr := host.NewScriptAddress() + + cleanupInput, err := script.WithPrecompileAtAddress[*DeployAsteriscInput](host, inputAddr, &input) + if err != nil { + return output, fmt.Errorf("failed to insert DeployAsteriscInput precompile: %w", err) + } + defer cleanupInput() + + cleanupOutput, err := script.WithPrecompileAtAddress[*DeployAsteriscOutput](host, outputAddr, &output, + script.WithFieldSetter[*DeployAsteriscOutput]) + if err != nil { + return output, fmt.Errorf("failed to insert DeployAsteriscOutput precompile: %w", err) + } + defer cleanupOutput() + + implContract := "DeployAsterisc" + deployScript, cleanupDeploy, err := script.WithScript[DeployAsteriscScript](host, "DeployAsterisc.s.sol", implContract) + if err != nil { + return output, fmt.Errorf("failed to load %s script: %w", implContract, err) + } + defer cleanupDeploy() + + if err := deployScript.Run(inputAddr, outputAddr); err != nil { + return output, fmt.Errorf("failed to run %s script: %w", implContract, err) + } + + return output, nil +} diff --git a/op-deployer/pkg/deployer/opcm/asterisc_test.go b/op-deployer/pkg/deployer/opcm/asterisc_test.go new file mode 100644 index 00000000000..0ed68f7aeaf --- /dev/null +++ b/op-deployer/pkg/deployer/opcm/asterisc_test.go @@ -0,0 +1,34 @@ +package opcm + +import ( + "testing" + + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/broadcaster" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/testutil" + "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" + "github.com/ethereum-optimism/optimism/op-service/testlog" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/stretchr/testify/require" +) + +func TestDeployAsterisc(t *testing.T) { + _, artifacts := testutil.LocalArtifacts(t) + + host, err := env.DefaultScriptHost( + broadcaster.NoopBroadcaster(), + testlog.Logger(t, log.LevelInfo), + common.Address{'D'}, + artifacts, + ) + require.NoError(t, err) + + input := DeployAsteriscInput{ + PreimageOracle: common.Address{0xab}, + } + + output, err := DeployAsterisc(host, input) + require.NoError(t, err) + + require.NotEmpty(t, output.AsteriscSingleton) +} diff --git a/op-deployer/pkg/deployer/opcm/dispute_game.go b/op-deployer/pkg/deployer/opcm/dispute_game.go index 0e117c040d8..481a401d07d 100644 --- a/op-deployer/pkg/deployer/opcm/dispute_game.go +++ b/op-deployer/pkg/deployer/opcm/dispute_game.go @@ -11,9 +11,7 @@ import ( type DeployDisputeGameInput struct { Release string StandardVersionsToml string - MipsVersion uint64 - MinProposalSizeBytes uint64 - ChallengePeriodSeconds uint64 + VmAddress common.Address GameKind string GameType uint32 AbsolutePrestate common.Hash @@ -33,9 +31,7 @@ func (input *DeployDisputeGameInput) InputSet() bool { } type DeployDisputeGameOutput struct { - DisputeGameImpl common.Address - MipsSingleton common.Address - PreimageOracleSingleton common.Address + DisputeGameImpl common.Address } func (output *DeployDisputeGameOutput) CheckOutput(input common.Address) error { diff --git a/op-deployer/pkg/deployer/opcm/dispute_game_test.go b/op-deployer/pkg/deployer/opcm/dispute_game_test.go index f39849be440..2b2d33f9efe 100644 --- a/op-deployer/pkg/deployer/opcm/dispute_game_test.go +++ b/op-deployer/pkg/deployer/opcm/dispute_game_test.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum-optimism/optimism/op-deployer/pkg/env" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/stretchr/testify/require" ) @@ -26,13 +27,15 @@ func TestDeployDisputeGame(t *testing.T) { standardVersionsTOML, err := standard.L1VersionsDataFor(11155111) require.NoError(t, err) + vmAddr := common.Address{'V'} + host.ImportAccount(vmAddr, types.Account{Code: vmCode}) + // Address has to match the one returned by vmCode for oracle()(address) + host.ImportAccount(common.HexToAddress("0x92240135b46fc1142dA181f550aE8f595B858854"), types.Account{Code: oracleCode}) input := DeployDisputeGameInput{ Release: "dev", StandardVersionsToml: standardVersionsTOML, - MipsVersion: 1, - MinProposalSizeBytes: standard.MinProposalSizeBytes, - ChallengePeriodSeconds: standard.ChallengePeriodSeconds, + VmAddress: vmAddr, GameKind: "PermissionedDisputeGame", GameType: 1, AbsolutePrestate: common.Hash{'A'}, @@ -51,6 +54,8 @@ func TestDeployDisputeGame(t *testing.T) { require.NoError(t, err) require.NotEmpty(t, output.DisputeGameImpl) - require.NotEmpty(t, output.MipsSingleton) - require.NotEmpty(t, output.PreimageOracleSingleton) } + +// Code to etch so that the VM oracle() method and the oracle challengePeriod() methods work (they return immutables) +var vmCode = common.FromHex("0x608060405234801561001057600080fd5b50600436106100415760003560e01c806354fd4d50146100465780637dc0d1d014610098578063e14ced32146100dc575b600080fd5b6100826040518060400160405280600c81526020017f312e322e312d626574612e37000000000000000000000000000000000000000081525081565b60405161008f919061269c565b60405180910390f35b60405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000092240135b46fc1142da181f550ae8f595b85885416815260200161008f565b6100ef6100ea366004612751565b6100fd565b60405190815260200161008f565b6000610107612612565b6080811461011457600080fd5b6040516106001461012457600080fd5b6084871461013157600080fd5b6101a4851461013f57600080fd5b8635608052602087013560a052604087013560e090811c60c09081526044890135821c82526048890135821c61010052604c890135821c610120526050890135821c61014052605489013590911c61016052605888013560f890811c610180526059890135901c6101a0819052605a89013590911c6101c05260628801906101e09060018111156101f5576040517f0136cc76000000000000000000000000000000000000000000000000000000008152600481fd5b506020810181511461020657600080fd5b60200160005b602081101561023057823560e01c825260049092019160209091019060010161020c565b5050508061012001511561024e57610246610408565b9150506103ff565b6101408101805160010167ffffffffffffffff16905260006101a4905060008060006102838560600151866000015186610559565b9250925092508163ffffffff1660001480156102a557508063ffffffff16600c145b156102bf576102b387610583565b955050505050506103ff565b63ffffffff8216603014806102da575063ffffffff82166038145b156102ea576102b385848461095d565b6000610364866040805160808101825260008082526020820181905291810182905260608101919091526040518060800160405280836060015163ffffffff168152602001836080015163ffffffff1681526020018360a0015163ffffffff1681526020018360c0015163ffffffff168152509050919050565b6040805160e0810182528281526101608901516020820152885191810191909152610524606082015263ffffffff808716608083015285811660a0830152841660c08201529091506103b581610b78565b50508752815163ffffffff9081166060808a01919091526020840151821660808a01526040840151821660a08a01528301511660c08801526103f5610408565b9750505050505050505b95945050505050565b60408051608051815260a051602082015260dc519181019190915260fc51604482015261011c51604882015261013c51604c82015261015c51605082015261017c5160548201526101805161019f5160588301526101a0516101bf5160598401526101d851605a84015260009261020092909160628301919060018111156104b5576040517f0136cc76000000000000000000000000000000000000000000000000000000008152600481fd5b60005b60208110156104dc57601c86015184526020909501946004909301926001016104b8565b506000835283830384a06000945080600181146104fc5760039550610524565b828015610514576001811461051d5760029650610522565b60009650610522565b600196505b505b50505081900390207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1660f89190911b17919050565b6000806000610569858786611001565b925050603f601a83901c8116915082165b93509350939050565b600061058d612612565b608090506000806000806105c48561016001516040810151608082015160a083015160c084015160e0909401519294919390929091565b509350935093509350600080610ffa63ffffffff168663ffffffff1603610609576105f485858960e00151611053565b63ffffffff1660e08a0152909250905061086c565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03363ffffffff871601610642576340000000915061086c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffefe863ffffffff871601610678576001915061086c565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef6a63ffffffff8716016106cc57600161012088015260ff85166101008801526106bf610408565b9998505050505050505050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff05d63ffffffff8716016107ca5760006040518061012001604052808763ffffffff1681526020018663ffffffff1681526020018563ffffffff16815260200189602001518152602001896040015163ffffffff1681526020018b81526020017f00000000000000000000000092240135b46fc1142da181f550ae8f595b85885473ffffffffffffffffffffffffffffffffffffffff16815260200161079a6101a4600160ff16610380020190565b8152895160209091015290506107af816110e7565b50508b5263ffffffff1660408b0152909350915061086c9050565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff05c63ffffffff87160161082f5760208701516040880151610815918791879187916105248d51611367565b63ffffffff1660408b015260208a0152909250905061086c565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff02963ffffffff87160161086c57610866858561145d565b90925090505b60006108e6886040805160808101825260008082526020820181905291810182905260608101919091526040518060800160405280836060015163ffffffff168152602001836080015163ffffffff1681526020018360a0015163ffffffff1681526020018360c0015163ffffffff168152509050919050565b61016089015163ffffffff85811660408084019190915285821660e0909301929092526020830180518083168086526004909101831682526060808e01919091529051821660808d015291830151811660a08c0152908201511660c08a0152905061094f610408565b9a9950505050505050505050565b610160830151600090601f601585901c169082908260208110610982576109826127c5565b60200201519050601f601086901c16600061099c8761159c565b905082810163fffffffc166000610524905060006109bf8b600001518484611001565b905060007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd063ffffffff8b16016109f7575080610a93565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc863ffffffff8b1601610a615760008c61016001518763ffffffff1660208110610a4357610a436127c5565b60200201519050610a558585836115b3565b8d525060019050610a93565b6040517fecf79d0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610b0d8d6040805160808101825260008082526020820181905291810182905260608101919091526040518060800160405280836060015163ffffffff168152602001836080015163ffffffff1681526020018360a0015163ffffffff1681526020018360c0015163ffffffff168152509050919050565b9050610b22818e610160015189856001611655565b610b5f8d82805163ffffffff9081166060808501919091526020830151821660808501526040830151821660a0850152909101511660c090910152565b610b67610408565b9d9c50505050505050505050505050565b604081015160a0820151600090819063ffffffff1660021480610ba557508360a0015163ffffffff166003145b15610bfe57608084015184516020808201519087015160a088015163f0000000909216630ffffffc600295861b161793610bf8939263ffffffff1614610bec57601f610bef565b60005b60ff168461172a565b50610ffa565b60808401516020808601516000928392601f601083901c8116939260151c16908110610c2c57610c2c6127c5565b602002015160a0880151909350819063ffffffff161580610c5757508760a0015163ffffffff16601c145b15610c985787602001518263ffffffff1660208110610c7857610c786127c5565b60200201519250600b886080015163ffffffff16901c601f169050610d77565b60208860a0015163ffffffff161015610d12578760a0015163ffffffff16600c1480610cce57508760a0015163ffffffff16600d145b80610ce357508760a0015163ffffffff16600e145b15610cf857876080015161ffff169250610d77565b610d0b886080015161ffff1660106117fd565b9250610d77565b60288860a0015163ffffffff16101580610d3657508760a0015163ffffffff166022145b80610d4b57508760a0015163ffffffff166026145b15610d775787602001518263ffffffff1660208110610d6c57610d6c6127c5565b602002015192508190505b60048860a0015163ffffffff1610158015610d9c575060088860a0015163ffffffff16105b80610db157508760a0015163ffffffff166001145b15610ddd57610dd4886000015189602001518a60a001518b608001518689611870565b50505050610ffa565b600063ffffffff9050600060208a60a0015163ffffffff1610610e4d57610e0d8a6080015161ffff1660106117fd565b8601955060008663fffffffc169050610e2f8b60400151828d60600151611001565b915060288b60a0015163ffffffff1610610e4b57809250600093505b505b6000610e698b608001518c60a001518d60c001518a8a87611ab3565b63ffffffff1690508a60a0015163ffffffff166000148015610e96575060088b60c0015163ffffffff1610155b8015610eac5750601c8b60c0015163ffffffff16105b15610fb2578a60c0015163ffffffff1660081480610ed457508a60c0015163ffffffff166009145b15610f1357610f078b600001518c602001518d60c0015163ffffffff16600814610efe5786610f01565b60005b8a61172a565b50505050505050610ffa565b8a60c0015163ffffffff16600a03610f40578a5160208c0151610f079190868a63ffffffff8b1615611655565b8a60c0015163ffffffff16600b03610f6e578a5160208c0151610f079190868a63ffffffff8b161515611655565b60108b60c0015163ffffffff1610158015610f935750601c8b60c0015163ffffffff16105b15610fb257610f078b600001518c602001518d60c001518a8a89612121565b8263ffffffff1663ffffffff14610fdc57610fd2838c60600151836115b3565b9950600198508297505b610ff28b600001518c6020015186846001611655565b505050505050505b9193909250565b60008061100f8585856123da565b90925090508061104b576040517f8e77b2b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b509392505050565b6000808284610fff81161561106d57610fff811661100003015b8663ffffffff166000036110d95784935090810190636000000063ffffffff831611806110a557508463ffffffff168263ffffffff16105b806110bb57508563ffffffff168163ffffffff16105b156110d4575063ffffffff92506016915083905061057a565b6110dd565b8693505b5093509350939050565b610100810151608082015182516000928392918390819063ffffffff161561135e57865163ffffffff167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffb01611318576000876020015163fffffffc169050600061115c896101000151838b60e00151611001565b60608a015190915060001a6001036111de576111d889606001518a60a0015160408051600093845233602052918152606090922091527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b60608a01525b6000808a60c0015173ffffffffffffffffffffffffffffffffffffffff1663e03110e18c606001518d608001516040518363ffffffff1660e01b815260040161123792919091825263ffffffff16602082015260400190565b6040805180830381865afa158015611253573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127791906127f4565b60208d015160408e0151929450909250906003821660048190038481101561129d578094505b50838210156112aa578193505b8460088502610100031c9450846008828660040303021b9450600180600883600403021b036001806008878560040303021b039150811981169050858119881617965050506112fe868e60e00151876115b3565b929b5050509689019695506001945091925061135e915050565b865163ffffffff167ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd01611352578660400151955061135e565b63ffffffff9550600994505b91939550919395565b600080858563ffffffff8b1660011480611387575063ffffffff8b166002145b80611398575063ffffffff8b166004145b156113a55788935061144f565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa63ffffffff8c16016114435760006113e5868c63fffffffc1689611001565b90508860038c166004038b8110156113fb57809b505b8b965086900360089081029290921c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600193880293841b0116911b1791506000905061144f565b63ffffffff9350600992505b975097509750979350505050565b60008063ffffffff83166001036114f95763ffffffff84161580611487575063ffffffff84166001145b80611498575063ffffffff84166002145b806114a9575063ffffffff84166005145b806114ba575063ffffffff84166003145b806114cb575063ffffffff84166006145b806114dc575063ffffffff84166004145b156114ea5760009150611595565b5063ffffffff90506009611595565b8263ffffffff1660030361158a5763ffffffff84161580611520575063ffffffff84166005145b80611531575063ffffffff84166003145b1561153f5760009150611595565b63ffffffff84166001148061155a575063ffffffff84166002145b8061156b575063ffffffff84166006145b8061157c575063ffffffff84166004145b156114ea5760019150611595565b5063ffffffff905060165b9250929050565b60006115ad8261ffff1660106117fd565b92915050565b60006115be83612485565b60038416156115cc57600080fd5b6020830192601f8516601c0360031b83811b913563ffffffff90911b1916178460051c60005b601b81101561164a5760208601953582821c600116801561161a576001811461162f57611640565b60008581526020839052604090209450611640565b600082815260208690526040902094505b50506001016115f2565b509095945050505050565b60208363ffffffff16106116ca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f76616c696420726567697374657200000000000000000000000000000000000060448201526064015b60405180910390fd5b63ffffffff8316158015906116dc5750805b1561170b5781848463ffffffff16602081106116fa576116fa6127c5565b63ffffffff90921660209290920201525b5050505060208101805163ffffffff8082169093526004019091169052565b836000015160040163ffffffff16846020015163ffffffff16146117aa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601260248201527f6a756d7020696e2064656c617920736c6f74000000000000000000000000000060448201526064016116c1565b835160208501805163ffffffff90811687528381169091528316156117f65780600801848463ffffffff16602081106117e5576117e56127c5565b63ffffffff90921660209290920201525b5050505050565b600063ffffffff8381167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80850183169190911c821615159160016020869003821681901b830191861691821b92911b018261185a57600061185c565b815b90861663ffffffff16179250505092915050565b6000866000015160040163ffffffff16876020015163ffffffff16146118f2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f6272616e636820696e2064656c617920736c6f7400000000000000000000000060448201526064016116c1565b8463ffffffff166004148061190d57508463ffffffff166005145b15611984576000868463ffffffff166020811061192c5761192c6127c5565b602002015190508063ffffffff168363ffffffff1614801561195457508563ffffffff166004145b8061197c57508063ffffffff168363ffffffff161415801561197c57508563ffffffff166005145b915050611a56565b8463ffffffff166006036119a15760008260030b13159050611a56565b8463ffffffff166007036119bd5760008260030b139050611a56565b8463ffffffff16600103611a5657601f601085901c1660008190036119e65760008360030b1291505b8063ffffffff16601003611a1057875160080163ffffffff166103e08801526000600384900b1291505b8063ffffffff16600103611a295760008360030b121591505b8063ffffffff16601103611a5457875160080163ffffffff166103e08801526000600384900b121591505b505b8651602088015163ffffffff1688528115611a97576002611a7c8661ffff1660106117fd565b63ffffffff90811690911b8201600401166020890152611aa9565b60208801805160040163ffffffff1690525b5050505050505050565b600063ffffffff86161580611ae0575060088663ffffffff1610158015611ae05750600f8663ffffffff16105b15611ee0578560088114611b235760098114611b2c57600a8114611b3557600b8114611b3e57600c8114611b4757600d8114611b5057600e8114611b5957611b5e565b60209550611b5e565b60219550611b5e565b602a9550611b5e565b602b9550611b5e565b60249550611b5e565b60259550611b5e565b602695505b508463ffffffff16600003611b83575063ffffffff8216601f600688901c161b612117565b8463ffffffff16600203611ba7575063ffffffff8216601f600688901c161c612117565b8463ffffffff16600303611bdb57601f600688901c16611bd363ffffffff8516821c60208390036117fd565b915050612117565b8463ffffffff16600403611bfb575063ffffffff8216601f84161b612117565b8463ffffffff16600603611c1b575063ffffffff8216601f84161c612117565b8463ffffffff16600703611c4357601f8416611bd363ffffffff8516821c60208390036117fd565b8463ffffffff16600803611c58575082612117565b8463ffffffff16600903611c6d575082612117565b8463ffffffff16600a03611c82575082612117565b8463ffffffff16600b03611c97575082612117565b8463ffffffff16600c03611cac575082612117565b8463ffffffff16600f03611cc1575082612117565b8463ffffffff16601003611cd6575082612117565b8463ffffffff16601103611ceb575082612117565b8463ffffffff16601203611d00575082612117565b8463ffffffff16601303611d15575082612117565b8463ffffffff16601803611d2a575082612117565b8463ffffffff16601903611d3f575082612117565b8463ffffffff16601a03611d54575082612117565b8463ffffffff16601b03611d69575082612117565b8463ffffffff16602003611d805750828201612117565b8463ffffffff16602103611d975750828201612117565b8463ffffffff16602203611dae5750818303612117565b8463ffffffff16602303611dc55750818303612117565b8463ffffffff16602403611ddc5750828216612117565b8463ffffffff16602503611df35750828217612117565b8463ffffffff16602603611e0a5750828218612117565b8463ffffffff16602703611e22575082821719612117565b8463ffffffff16602a03611e51578260030b8460030b12611e44576000611e47565b60015b60ff169050612117565b8463ffffffff16602b03611e79578263ffffffff168463ffffffff1610611e44576000611e47565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f696e76616c696420696e737472756374696f6e0000000000000000000000000060448201526064016116c1565b611e79565b8563ffffffff16601c03611f60578463ffffffff16600203611f055750828202612117565b8463ffffffff1660201480611f2057508463ffffffff166021145b15611edb578463ffffffff16602003611f37579219925b60005b6380000000851615611f59576401fffffffe600195861b169401611f3a565b9050612117565b8563ffffffff16600f03611f81575065ffffffff0000601083901b16612117565b8563ffffffff16602003611f9c57611f59848360018061251e565b8563ffffffff16602103611fb857611f5984836002600161251e565b8563ffffffff16602203611fe6575063ffffffff60086003851602811681811b198416918316901b17612117565b8563ffffffff1660230361200257611f5984836004600161251e565b8563ffffffff1660240361201e57611f5984836001600061251e565b8563ffffffff1660250361203a57611f5984836002600061251e565b8563ffffffff1660260361206b575063ffffffff60086003851602601803811681811c198416918316901c17612117565b8563ffffffff1660280361208657611f598483600186612566565b8563ffffffff166029036120a157611f598483600286612566565b8563ffffffff16602a036120cf575063ffffffff60086003851602811681811c198316918416901c17612117565b8563ffffffff16602b036120ea57611f598483600486612566565b8563ffffffff16602e03611e79575063ffffffff60086003851602601803811681811b198316918416901b175b9695505050505050565b60008463ffffffff1660100361213c57506060860151612382565b8463ffffffff1660110361215b5763ffffffff84166060880152612382565b8463ffffffff1660120361217457506040860151612382565b8463ffffffff166013036121935763ffffffff84166040880152612382565b8463ffffffff166018036121c75763ffffffff600385810b9085900b02602081901c821660608a0152166040880152612382565b8463ffffffff166019036121f85763ffffffff84811681851602602081901c821660608a0152166040880152612382565b8463ffffffff16601a036122bb578260030b600003612273576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d4950533a206469766973696f6e206279207a65726f0000000000000000000060448201526064016116c1565b8260030b8460030b8161228857612288612818565b0763ffffffff166060880152600383810b9085900b816122aa576122aa612818565b0563ffffffff166040880152612382565b8463ffffffff16601b03612382578263ffffffff16600003612339576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d4950533a206469766973696f6e206279207a65726f0000000000000000000060448201526064016116c1565b8263ffffffff168463ffffffff168161235457612354612818565b0663ffffffff90811660608901528381169085168161237557612375612818565b0463ffffffff1660408801525b63ffffffff8216156123b85780868363ffffffff16602081106123a7576123a76127c5565b63ffffffff90921660209290920201525b50505060208401805163ffffffff808216909652600401909416909352505050565b6000806123e683612485565b60038416156123f457600080fd5b6020830192358460051c8160005b601b81101561245a5760208701963583821c600116801561242a576001811461243f57612450565b60008481526020839052604090209350612450565b600082815260208590526040902093505b5050600101612402565b508714925050811561247c57601f8516601c0360031b81901c63ffffffff1692505b50935093915050565b36610380820181101561251a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f636865636b207468617420746865726520697320656e6f7567682063616c6c6460448201527f617461000000000000000000000000000000000000000000000000000000000060648201526084016116c1565b5050565b60008060008061252e888761259a565b925092509250828263ffffffff168863ffffffff16901c169350841561255b5761255884826117fd565b93505b505050949350505050565b6000806000612575878661259a565b5063ffffffff868316811691811691821b9216901b1987161792505050949350505050565b600080806407fffffff8600385901b16816125b6826020612847565b63ffffffff9081161c905060006125ce600188612847565b198816600316905060006125e3886004612847565b9050888216600060036125f68385612847565b959c63ffffffff909616901b9a50949850929650505050505050565b6040805161018081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c0810182905260e0810182905261010081018290526101208101829052610140810191909152610160810161267861267d565b905290565b6040518061040001604052806020906020820280368337509192915050565b600060208083528351808285015260005b818110156126c9578581018301518582016040015282016126ad565b818111156126db576000604083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016929092016040019392505050565b60008083601f84011261272157600080fd5b50813567ffffffffffffffff81111561273957600080fd5b60208301915083602082850101111561159557600080fd5b60008060008060006060868803121561276957600080fd5b853567ffffffffffffffff8082111561278157600080fd5b61278d89838a0161270f565b909750955060208801359150808211156127a657600080fd5b506127b38882890161270f565b96999598509660400135949350505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000806040838503121561280757600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600063ffffffff8381169083168181101561288b577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b03939250505056fea164736f6c634300080f000a") +var oracleCode = common.FromHex("0x6080604052600436106101d85760003560e01c80639d53a64811610102578063ddcd58de11610095578063ec5efcbc11610064578063ec5efcbc14610681578063f3f480d9146106a1578063faf37bc7146106d4578063fef2b4ed146106e757600080fd5b8063ddcd58de146105d4578063e03110e11461060c578063e159261114610641578063ea7139501461066157600080fd5b8063b5e7154c116100d1578063b5e7154c14610555578063d18534b51461056c578063da35c6641461058c578063dd24f9bf146105a157600080fd5b80639d53a6481461048e5780639d7e8769146104dd578063b2e67ba8146104fd578063b4801e611461053557600080fd5b806361238bde1161017a5780637ac54767116101495780637ac54767146103ca5780638542cf50146103ea578063882856ef146104355780638dc4be111461046e57600080fd5b806361238bde1461031e5780636551927b146103565780637051472e1461038e5780637917de1d146103aa57600080fd5b80633909af5c116101b65780633909af5c146102715780634d52b4c91461029357806352f0f3ad146102a857806354fd4d50146102c857600080fd5b8063013cf08b146101dd5780630359a5631461022e5780632055b36b1461025c575b600080fd5b3480156101e957600080fd5b506101fd6101f8366004612e29565b610714565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526020830191909152015b60405180910390f35b34801561023a57600080fd5b5061024e610249366004612e6b565b610759565b604051908152602001610225565b34801561026857600080fd5b5061024e601081565b34801561027d57600080fd5b5061029161028c366004613073565b610891565b005b34801561029f57600080fd5b5061024e610ae8565b3480156102b457600080fd5b5061024e6102c336600461315f565b610b03565b3480156102d457600080fd5b506103116040518060400160405280600581526020017f312e312e3200000000000000000000000000000000000000000000000000000081525081565b60405161022591906131c6565b34801561032a57600080fd5b5061024e610339366004613217565b600160209081526000928352604080842090915290825290205481565b34801561036257600080fd5b5061024e610371366004612e6b565b601560209081526000928352604080842090915290825290205481565b34801561039a57600080fd5b5061024e6703782dace9d9000081565b3480156103b657600080fd5b506102916103c536600461327b565b610bd9565b3480156103d657600080fd5b5061024e6103e5366004612e29565b6110dc565b3480156103f657600080fd5b50610425610405366004613217565b600260209081526000928352604080842090915290825290205460ff1681565b6040519015158152602001610225565b34801561044157600080fd5b50610455610450366004613317565b6110f3565b60405167ffffffffffffffff9091168152602001610225565b34801561047a57600080fd5b5061029161048936600461334a565b61114d565b34801561049a57600080fd5b5061024e6104a9366004612e6b565b73ffffffffffffffffffffffffffffffffffffffff9091166000908152601860209081526040808320938352929052205490565b3480156104e957600080fd5b506102916104f8366004613396565b611248565b34801561050957600080fd5b5061024e610518366004612e6b565b601760209081526000928352604080842090915290825290205481565b34801561054157600080fd5b5061024e610550366004613317565b6113ff565b34801561056157600080fd5b5061024e620186a081565b34801561057857600080fd5b50610291610587366004613073565b611431565b34801561059857600080fd5b5060135461024e565b3480156105ad57600080fd5b507f000000000000000000000000000000000000000000000000000000000001ec3061024e565b3480156105e057600080fd5b5061024e6105ef366004612e6b565b601660209081526000928352604080842090915290825290205481565b34801561061857600080fd5b5061062c610627366004613217565b611840565b60408051928352602083019190915201610225565b34801561064d57600080fd5b5061029161065c36600461334a565b611931565b34801561066d57600080fd5b5061029161067c366004613422565b611a39565b34801561068d57600080fd5b5061029161069c366004613491565b611b98565b3480156106ad57600080fd5b507f000000000000000000000000000000000000000000000000000000000001518061024e565b6102916106e2366004613519565b611d1e565b3480156106f357600080fd5b5061024e610702366004612e29565b60006020819052908152604090205481565b6013818154811061072457600080fd5b60009182526020909120600290910201805460019091015473ffffffffffffffffffffffffffffffffffffffff909116915082565b73ffffffffffffffffffffffffffffffffffffffff82166000908152601560209081526040808320848452909152812054819061079c9060601c63ffffffff1690565b63ffffffff16905060005b6010811015610889578160011660010361082f5773ffffffffffffffffffffffffffffffffffffffff85166000908152601460209081526040808320878452909152902081601081106107fc576107fc613555565b01546040805160208101929092528101849052606001604051602081830303815290604052805190602001209250610870565b826003826010811061084357610843613555565b01546040805160208101939093528201526060016040516020818303038152906040528051906020012092505b60019190911c9080610881816135b3565b9150506107a7565b505092915050565b600061089d8a8a610759565b90506108c086868360208b01356108bb6108b68d6135eb565b611fea565b61202a565b80156108de57506108de83838360208801356108bb6108b68a6135eb565b610914576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b86604001358860405160200161092a91906136ba565b6040516020818303038152906040528051906020012014610977576040517f1968a90200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83602001358760200135600161098d91906136f8565b146109c4576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610a0c886109d28680613710565b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061208b92505050565b610a15886121e6565b836040013588604051602001610a2b91906136ba565b6040516020818303038152906040528051906020012003610a78576040517f9843145b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8a1660009081526015602090815260408083208c8452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001179055610adc8a8a3361298e565b50505050505050505050565b6001610af660106002613897565b610b0091906138a3565b81565b6000610b0f8686612a47565b9050610b1c8360086136f8565b82101580610b2a5750602083115b15610b61576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602081815260c085901b82526008959095528251828252600286526040808320858452875280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558484528752808320948352938652838220558181529384905292205592915050565b60608115610bf257610beb8686612af4565b9050610c2c565b85858080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509293505050505b3360009081526014602090815260408083208b845290915280822081516102008101928390529160109082845b815481526020019060010190808311610c5957505050505090506000601560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008b81526020019081526020016000205490506000610cda8260601c63ffffffff1690565b63ffffffff169050333214610d1b576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d2b8260801c63ffffffff1690565b63ffffffff16600003610d6a576040517f87138d5c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d748260c01c90565b67ffffffffffffffff1615610db5576040517f475a253500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b898114610dee576040517f60f95d5a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610dfb89898d8886612b6d565b83516020850160888204881415608883061715610e20576307b1daf16000526004601cfd5b60405160c8810160405260005b83811015610ed0578083018051835260208101516020840152604081015160408401526060810151606084015260808101516080840152508460888301526088810460051b8b013560a883015260c882206001860195508560005b610200811015610ec5576001821615610ea55782818b0152610ec5565b8981015160009081526020938452604090209260019290921c9101610e88565b505050608801610e2d565b50505050600160106002610ee49190613897565b610eee91906138a3565b811115610f27576040517f6229572300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610f9c610f3a8360401c63ffffffff1690565b610f4a9063ffffffff168a6136f8565b60401b7fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff606084901b167fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff8516171790565b915084156110295777ffffffffffffffffffffffffffffffffffffffffffffffff82164260c01b179150610fd68260801c63ffffffff1690565b63ffffffff16610fec8360401c63ffffffff1690565b63ffffffff1614611029576040517f7b1dafd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526014602090815260408083208e8452909152902061104f90846010612d9f565b503360008181526018602090815260408083208f8452825280832080546001810182559084528284206004820401805460039092166008026101000a67ffffffffffffffff818102199093164390931602919091179055838352601582528083208f8452909152812084905560609190911b81523690601437366014016000a05050505050505050505050565b600381601081106110ec57600080fd5b0154905081565b6018602052826000526040600020602052816000526040600020818154811061111b57600080fd5b906000526020600020906004918282040191900660080292509250509054906101000a900467ffffffffffffffff1681565b60443560008060088301861061116b5763fe2549876000526004601cfd5b60c083901b60805260888386823786600882030151915060206000858360025afa90508061119857600080fd5b50600080517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0400000000000000000000000000000000000000000000000000000000000000178082526002602090815260408084208a8552825280842080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091558385528252808420998452988152888320939093558152908190529490942055505050565b600080603087600037602060006030600060025afa806112705763f91129696000526004601cfd5b6000517effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f010000000000000000000000000000000000000000000000000000000000000017608081815260a08c905260c08b905260308a60e037603088609083013760008060c083600a5afa9250826112f2576309bde3396000526004601cfd5b602886106113085763fe2549876000526004601cfd5b6000602882015278200000000000000000000000000000000000000000000000008152600881018b905285810151935060308a8237603081019b909b52505060509098207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0500000000000000000000000000000000000000000000000000000000000000176000818152600260209081526040808320868452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209583529481528482209a909a559081528089529190912096909655505050505050565b6014602052826000526040600020602052816000526040600020816010811061142757600080fd5b0154925083915050565b73ffffffffffffffffffffffffffffffffffffffff891660009081526015602090815260408083208b845290915290205467ffffffffffffffff8116156114a4576040517fc334f06900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6114ae8160c01c90565b67ffffffffffffffff166000036114f1576040517f55d4cbf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000001518061151c8260c01c90565b6115309067ffffffffffffffff16426138a3565b11611567576040517f55d4cbf900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006115738b8b610759565b905061158c87878360208c01356108bb6108b68e6135eb565b80156115aa57506115aa84848360208901356108bb6108b68b6135eb565b6115e0576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8760400135896040516020016115f691906136ba565b6040516020818303038152906040528051906020012014611643576040517f1968a90200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84602001358860200135600161165991906136f8565b14158061168b575060016116738360601c63ffffffff1690565b61167d91906138ba565b63ffffffff16856020013514155b156116c2576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6116d0896109d28780613710565b6116d9896121e6565b60006116e48a612cc0565b7effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0200000000000000000000000000000000000000000000000000000000000000179050600061173b8460a01c63ffffffff1690565b67ffffffffffffffff169050600160026000848152602001908152602001600020600083815260200190815260200160002060006101000a81548160ff021916908315150217905550601760008e73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008d8152602001908152602001600020546001600084815260200190815260200160002060008381526020019081526020016000208190555061180d8460801c63ffffffff1690565b600083815260208190526040902063ffffffff9190911690556118318d8d8161298e565b50505050505050505050505050565b6000828152600260209081526040808320848452909152812054819060ff166118c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f7072652d696d616765206d757374206578697374000000000000000000000000604482015260640160405180910390fd5b50600083815260208181526040909120546118e58160086136f8565b6118f08560206136f8565b1061190e57836119018260086136f8565b61190b91906138a3565b91505b506000938452600160209081526040808620948652939052919092205492909150565b60443560008060088301861061194f5763fe2549876000526004601cfd5b60c083901b6080526088838682378087017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80151908490207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f02000000000000000000000000000000000000000000000000000000000000001760008181526002602090815260408083208b8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915584845282528083209a83529981528982209390935590815290819052959095209190915550505050565b60008060008060808860601b81528760c01b6014820152858782601c0137601c860181207effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f0600000000000000000000000000000000000000000000000000000000000000179350604088026260216001603f5a021015611ac35763dd629f866000526004601cfd5b6000808783601c018c5afa94503d6001019150600882018a10611aee5763fe2549876000526004601cfd5b60c082901b81526008018481533d6000600183013e89017ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8015160008481526002602090815260408083208d8452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915587845282528083209c83529b81528b8220929092559384528390529790912096909655505050505050565b6000611ba48686610759565b9050611bbd83838360208801356108bb6108b68a6135eb565b611bf3576040517f09bde33900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b602084013515611c2f576040517f9a3b119900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c37612ddd565b611c45816109d28780613710565b611c4e816121e6565b846040013581604051602001611c6491906136ba565b6040516020818303038152906040528051906020012003611cb1576040517f9843145b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87166000908152601560209081526040808320898452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000166001179055611d1587873361298e565b50505050505050565b6703782dace9d90000341015611d60576040517fe92c469f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b333214611d99576040517fba092d1600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611da48160086138df565b63ffffffff168263ffffffff1610611de8576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f000000000000000000000000000000000000000000000000000000000001ec308163ffffffff161015611e48576040517f7b1dafd100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b336000908152601560209081526040808320868452909152902054611e738160801c63ffffffff1690565b63ffffffff1615611eb0576040517f0dc149f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b608082901b7fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff60a085901b167fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff83161717336000818152601560209081526040808320898452825280832094909455835180850185528381528082018981526013805460018101825590855291517f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a090600290930292830180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909216919091179055517f66de8ffda797e3de9c05e8fc57b3bf0ec28a930d40b0d285d93c06501cf6a0919091015591815260168252828120968152959052909320349055505050565b600081600001518260200151836040015160405160200161200d93929190613907565b604051602081830303815290604052805190602001209050919050565b60008160005b601081101561207e578060051b880135600186831c16600181146120635760008481526020839052604090209350612074565b600082815260208590526040902093505b5050600101612030565b5090931495945050505050565b608881511461209957600080fd5b602081016020830161211a565b8260031b8201518060001a8160011a60081b178160021a60101b8260031a60181b17178160041a60201b8260051a60281b178260061a60301b8360071a60381b1717179050612114816120ff868560059190911b015190565b1867ffffffffffffffff16600586901b840152565b50505050565b612126600083836120a6565b612132600183836120a6565b61213e600283836120a6565b61214a600383836120a6565b612156600483836120a6565b612162600583836120a6565b61216e600683836120a6565b61217a600783836120a6565b612186600883836120a6565b612192600983836120a6565b61219e600a83836120a6565b6121aa600b83836120a6565b6121b6600c83836120a6565b6121c2600d83836120a6565b6121ce600e83836120a6565b6121da600f83836120a6565b612114601083836120a6565b6040805178010000000000008082800000000000808a8000000080008000602082015279808b00000000800000018000000080008081800000000000800991810191909152788a00000000000000880000000080008009000000008000000a60608201527b8000808b800000000000008b8000000000008089800000000000800360808201527f80000000000080028000000000000080000000000000800a800000008000000a60a08201527f800000008000808180000000000080800000000080000001800000008000800860c082015260009060e0016040516020818303038152906040529050602082016020820161286e565b6102808101516101e082015161014083015160a0840151845118189118186102a082015161020083015161016084015160c0850151602086015118189118186102c083015161022084015161018085015160e0860151604087015118189118186102e08401516102408501516101a0860151610100870151606088015118189118186103008501516102608601516101c0870151610120880151608089015118189118188084603f1c6123998660011b67ffffffffffffffff1690565b18188584603f1c6123b48660011b67ffffffffffffffff1690565b18188584603f1c6123cf8660011b67ffffffffffffffff1690565b181895508483603f1c6123ec8560011b67ffffffffffffffff1690565b181894508387603f1c6124098960011b67ffffffffffffffff1690565b60208b01518b51861867ffffffffffffffff168c5291189190911897508118600181901b603f9190911c18935060c08801518118601481901c602c9190911b1867ffffffffffffffff1660208901526101208801518718602c81901c60149190911b1867ffffffffffffffff1660c08901526102c08801518618600381901c603d9190911b1867ffffffffffffffff166101208901526101c08801518718601981901c60279190911b1867ffffffffffffffff166102c08901526102808801518218602e81901c60129190911b1867ffffffffffffffff166101c089015260408801518618600281901c603e9190911b1867ffffffffffffffff166102808901526101808801518618601581901c602b9190911b1867ffffffffffffffff1660408901526101a08801518518602781901c60199190911b1867ffffffffffffffff166101808901526102608801518718603881901c60089190911b1867ffffffffffffffff166101a08901526102e08801518518600881901c60389190911b1867ffffffffffffffff166102608901526101e08801518218601781901c60299190911b1867ffffffffffffffff166102e089015260808801518718602581901c601b9190911b1867ffffffffffffffff166101e08901526103008801518718603281901c600e9190911b1867ffffffffffffffff1660808901526102a08801518118603e81901c60029190911b1867ffffffffffffffff166103008901526101008801518518600981901c60379190911b1867ffffffffffffffff166102a08901526102008801518118601381901c602d9190911b1867ffffffffffffffff1661010089015260a08801518218601c81901c60249190911b1867ffffffffffffffff1661020089015260608801518518602481901c601c9190911b1867ffffffffffffffff1660a08901526102408801518518602b81901c60159190911b1867ffffffffffffffff1660608901526102208801518618603181901c600f9190911b1867ffffffffffffffff166102408901526101608801518118603681901c600a9190911b1867ffffffffffffffff166102208901525060e08701518518603a81901c60069190911b1867ffffffffffffffff166101608801526101408701518118603d81901c60039190911b1867ffffffffffffffff1660e0880152505067ffffffffffffffff81166101408601525b5050505050565b600582811b8201805160018501831b8401805160028701851b8601805160038901871b8801805160048b0190981b8901805167ffffffffffffffff861985168918811690995283198a16861889169096528819861683188816909352841986168818871690528419831684189095169052919391929190611d15565b612808600082612781565b612813600582612781565b61281e600a82612781565b612829600f82612781565b612834601482612781565b50565b612840816122dc565b612849816127fd565b600383901b820151815160c09190911c9061211490821867ffffffffffffffff168352565b61287a60008284612837565b61288660018284612837565b61289260028284612837565b61289e60038284612837565b6128aa60048284612837565b6128b660058284612837565b6128c260068284612837565b6128ce60078284612837565b6128da60088284612837565b6128e660098284612837565b6128f2600a8284612837565b6128fe600b8284612837565b61290a600c8284612837565b612916600d8284612837565b612922600e8284612837565b61292e600f8284612837565b61293a60108284612837565b61294660118284612837565b61295260128284612837565b61295e60138284612837565b61296a60148284612837565b61297660158284612837565b61298260168284612837565b61211460178284612837565b73ffffffffffffffffffffffffffffffffffffffff83811660009081526016602090815260408083208684529091528082208054908390559051909284169083908381818185875af1925050503d8060008114612a07576040519150601f19603f3d011682016040523d82523d6000602084013e612a0c565b606091505b505090508061277a576040517f83e6cc6b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f01000000000000000000000000000000000000000000000000000000000000007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831617612aed818360408051600093845233602052918152606090922091527effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f01000000000000000000000000000000000000000000000000000000000000001790565b9392505050565b6060604051905081602082018181018286833760888306808015612b3d5760888290038501848101848103803687375060806001820353506001845160001a1784538652612b54565b608836843760018353608060878401536088850186525b5050505050601f19603f82510116810160405292915050565b6000612b7f8260a01c63ffffffff1690565b67ffffffffffffffff1690506000612b9d8360801c63ffffffff1690565b63ffffffff1690506000612bb78460401c63ffffffff1690565b63ffffffff169050600883108015612bcd575080155b15612c015760c082901b6000908152883560085283513382526017602090815260408084208a855290915290912055612cb6565b60088310158015612c1f575080612c196008856138a3565b93508310155b8015612c335750612c3087826136f8565b83105b15612cb6576000612c4482856138a3565b905087612c528260206136f8565b10158015612c5e575085155b15612c95576040517ffe25498700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3360009081526017602090815260408083208a845290915290209089013590555b5050505050505050565b6000612d43565b66ff00ff00ff00ff8160081c1667ff00ff00ff00ff00612cf18360081b67ffffffffffffffff1690565b1617905065ffff0000ffff8160101c1667ffff0000ffff0000612d1e8360101b67ffffffffffffffff1690565b1617905060008160201c612d3c8360201b67ffffffffffffffff1690565b1792915050565b60808201516020830190612d5b90612cc7565b612cc7565b6040820151612d6990612cc7565b60401b17612d81612d5660018460059190911b015190565b825160809190911b90612d9390612cc7565b60c01b17179392505050565b8260108101928215612dcd579160200282015b82811115612dcd578251825591602001919060010190612db2565b50612dd9929150612df5565b5090565b6040518060200160405280612df0612e0a565b905290565b5b80821115612dd95760008155600101612df6565b6040518061032001604052806019906020820280368337509192915050565b600060208284031215612e3b57600080fd5b5035919050565b803573ffffffffffffffffffffffffffffffffffffffff81168114612e6657600080fd5b919050565b60008060408385031215612e7e57600080fd5b612e8783612e42565b946020939093013593505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610320810167ffffffffffffffff81118282101715612ee857612ee8612e95565b60405290565b6040516060810167ffffffffffffffff81118282101715612ee857612ee8612e95565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715612f5857612f58612e95565b604052919050565b803567ffffffffffffffff81168114612e6657600080fd5b6000610320808385031215612f8c57600080fd5b604051602080820182811067ffffffffffffffff82111715612fb057612fb0612e95565b806040525081935085601f860112612fc757600080fd5b612fcf612ec4565b928501928087851115612fe157600080fd5b865b8581101561300157612ff481612f60565b8352918301918301612fe3565b509092525091949350505050565b60006060828403121561302157600080fd5b50919050565b60008083601f84011261303957600080fd5b50813567ffffffffffffffff81111561305157600080fd5b6020830191508360208260051b850101111561306c57600080fd5b9250929050565b60008060008060008060008060006103e08a8c03121561309257600080fd5b61309b8a612e42565b985060208a013597506130b18b60408c01612f78565b96506103608a013567ffffffffffffffff808211156130cf57600080fd5b6130db8d838e0161300f565b97506103808c01359150808211156130f257600080fd5b6130fe8d838e01613027565b90975095506103a08c013591508082111561311857600080fd5b6131248d838e0161300f565b94506103c08c013591508082111561313b57600080fd5b506131488c828d01613027565b915080935050809150509295985092959850929598565b600080600080600060a0868803121561317757600080fd5b505083359560208501359550604085013594606081013594506080013592509050565b60005b838110156131b557818101518382015260200161319d565b838111156121145750506000910152565b60208152600082518060208401526131e581604085016020870161319a565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b6000806040838503121561322a57600080fd5b50508035926020909101359150565b60008083601f84011261324b57600080fd5b50813567ffffffffffffffff81111561326357600080fd5b60208301915083602082850101111561306c57600080fd5b600080600080600080600060a0888a03121561329657600080fd5b8735965060208801359550604088013567ffffffffffffffff808211156132bc57600080fd5b6132c88b838c01613239565b909750955060608a01359150808211156132e157600080fd5b506132ee8a828b01613027565b9094509250506080880135801515811461330757600080fd5b8091505092959891949750929550565b60008060006060848603121561332c57600080fd5b61333584612e42565b95602085013595506040909401359392505050565b60008060006040848603121561335f57600080fd5b83359250602084013567ffffffffffffffff81111561337d57600080fd5b61338986828701613239565b9497909650939450505050565b600080600080600080600060a0888a0312156133b157600080fd5b8735965060208801359550604088013567ffffffffffffffff808211156133d757600080fd5b6133e38b838c01613239565b909750955060608a01359150808211156133fc57600080fd5b506134098a828b01613239565b989b979a50959894979596608090950135949350505050565b60008060008060006080868803121561343a57600080fd5b8535945061344a60208701612e42565b935061345860408701612f60565b9250606086013567ffffffffffffffff81111561347457600080fd5b61348088828901613239565b969995985093965092949392505050565b6000806000806000608086880312156134a957600080fd5b6134b286612e42565b945060208601359350604086013567ffffffffffffffff808211156134d657600080fd5b6134e289838a0161300f565b945060608801359150808211156134f857600080fd5b5061348088828901613027565b803563ffffffff81168114612e6657600080fd5b60008060006060848603121561352e57600080fd5b8335925061353e60208501613505565b915061354c60408501613505565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036135e4576135e4613584565b5060010190565b6000606082360312156135fd57600080fd5b613605612eee565b823567ffffffffffffffff8082111561361d57600080fd5b9084019036601f83011261363057600080fd5b813560208282111561364457613644612e95565b613674817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601612f11565b9250818352368183860101111561368a57600080fd5b81818501828501376000918301810191909152908352848101359083015250604092830135928101929092525090565b81516103208201908260005b60198110156136ef57825167ffffffffffffffff168252602092830192909101906001016136c6565b50505092915050565b6000821982111561370b5761370b613584565b500190565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261374557600080fd5b83018035915067ffffffffffffffff82111561376057600080fd5b60200191503681900382131561306c57600080fd5b600181815b808511156137ce57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156137b4576137b4613584565b808516156137c157918102915b93841c939080029061377a565b509250929050565b6000826137e557506001613891565b816137f257506000613891565b816001811461380857600281146138125761382e565b6001915050613891565b60ff84111561382357613823613584565b50506001821b613891565b5060208310610133831016604e8410600b8410161715613851575081810a613891565b61385b8383613775565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561388d5761388d613584565b0290505b92915050565b6000612aed83836137d6565b6000828210156138b5576138b5613584565b500390565b600063ffffffff838116908316818110156138d7576138d7613584565b039392505050565b600063ffffffff8083168185168083038211156138fe576138fe613584565b01949350505050565b6000845161391981846020890161319a565b9190910192835250602082015260400191905056fea164736f6c634300080f000a") diff --git a/op-deployer/pkg/deployer/pipeline/init.go b/op-deployer/pkg/deployer/pipeline/init.go index 88caf760f41..f7be4eae03d 100644 --- a/op-deployer/pkg/deployer/pipeline/init.go +++ b/op-deployer/pkg/deployer/pipeline/init.go @@ -141,10 +141,10 @@ func displayWarning() error { ####################### WARNING! WARNING WARNING! ####################### You are deploying a tagged release to a chain with no pre-deployed OPCM. -The contracts you are deploying may not be audited, or match a governance +The contracts you are deploying may not be audited, or match a governance approved release. -USE OF THIS DEPLOYMENT IS NOT RECOMMENDED FOR PRODUCTION. USE AT YOUR OWN +USE OF THIS DEPLOYMENT IS NOT RECOMMENDED FOR PRODUCTION. USE AT YOUR OWN RISK. BUGS OR LOSS OF FUNDS MAY OCCUR. WE HOPE YOU KNOW WHAT YOU ARE DOING. diff --git a/op-deployer/pkg/deployer/standard/standard.go b/op-deployer/pkg/deployer/standard/standard.go index 75902424ac7..d2fe5e5d942 100644 --- a/op-deployer/pkg/deployer/standard/standard.go +++ b/op-deployer/pkg/deployer/standard/standard.go @@ -26,6 +26,9 @@ const ( DisputeSplitDepth uint64 = 30 DisputeClockExtension uint64 = 10800 DisputeMaxClockDuration uint64 = 302400 + Eip1559DenominatorCanyon uint64 = 250 + Eip1559Denominator uint64 = 50 + Eip1559Elasticity uint64 = 6 ContractsV160Tag = "op-contracts/v1.6.0" ContractsV170Beta1L2Tag = "op-contracts/v1.7.0-beta.1+l2-contracts" @@ -97,6 +100,28 @@ func L1VersionsFor(chainID uint64) (L1Versions, error) { } } +func GuardianAddressFor(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + return common.HexToAddress("0x09f7150D8c019BeF34450d6920f6B3608ceFdAf2"), nil + case 11155111: + return common.HexToAddress("0x7a50f00e8D05b95F98fE38d8BeE366a7324dCf7E"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + +func ChallengerAddressFor(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + return common.HexToAddress("0x9BA6e03D8B90dE867373Db8cF1A58d2F7F006b3A"), nil + case 11155111: + return common.HexToAddress("0xfd1D2e729aE8eEe2E146c033bf4400fE75284301"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + func SuperchainFor(chainID uint64) (*superchain.Superchain, error) { switch chainID { case 1: @@ -115,7 +140,7 @@ func ChainNameFor(chainID uint64) (string, error) { case 11155111: return "sepolia", nil default: - return "", fmt.Errorf("unrecognized chain ID: %d", chainID) + return "", fmt.Errorf("unrecognized l1 chain ID: %d", chainID) } } @@ -173,6 +198,17 @@ func SystemOwnerAddrFor(chainID uint64) (common.Address, error) { } } +func L1ProxyAdminOwner(chainID uint64) (common.Address, error) { + switch chainID { + case 1: + return common.HexToAddress("0x5a0Aae59D09fccBdDb6C6CcEB07B7279367C3d2A"), nil + case 11155111: + return common.HexToAddress("0x1Eb2fFc903729a0F03966B917003800b145F56E2"), nil + default: + return common.Address{}, fmt.Errorf("unsupported chain ID: %d", chainID) + } +} + func ArtifactsURLForTag(tag string) (*url.URL, error) { switch tag { case "op-contracts/v1.6.0": diff --git a/op-deployer/pkg/deployer/state/chain_intent.go b/op-deployer/pkg/deployer/state/chain_intent.go new file mode 100644 index 00000000000..bb6693f56b8 --- /dev/null +++ b/op-deployer/pkg/deployer/state/chain_intent.go @@ -0,0 +1,82 @@ +package state + +import ( + "fmt" + "reflect" + + "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "github.com/ethereum/go-ethereum/common" +) + +type ChainIntent struct { + ID common.Hash `json:"id" toml:"id"` + BaseFeeVaultRecipient common.Address `json:"baseFeeVaultRecipient" toml:"baseFeeVaultRecipient"` + L1FeeVaultRecipient common.Address `json:"l1FeeVaultRecipient" toml:"l1FeeVaultRecipient"` + SequencerFeeVaultRecipient common.Address `json:"sequencerFeeVaultRecipient" toml:"sequencerFeeVaultRecipient"` + Eip1559DenominatorCanyon uint64 `json:"eip1559DenominatorCanyon" toml:"eip1559DenominatorCanyon"` + Eip1559Denominator uint64 `json:"eip1559Denominator" toml:"eip1559Denominator"` + Eip1559Elasticity uint64 `json:"eip1559Elasticity" toml:"eip1559Elasticity"` + Roles ChainRoles `json:"roles" toml:"roles"` + DeployOverrides map[string]any `json:"deployOverrides" toml:"deployOverrides"` + DangerousAltDAConfig genesis.AltDADeployConfig `json:"dangerousAltDAConfig,omitempty" toml:"dangerousAltDAConfig,omitempty"` +} + +type ChainRoles struct { + L1ProxyAdminOwner common.Address `json:"l1ProxyAdminOwner" toml:"l1ProxyAdminOwner"` + L2ProxyAdminOwner common.Address `json:"l2ProxyAdminOwner" toml:"l2ProxyAdminOwner"` + SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"` + UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"` + Batcher common.Address `json:"batcher" toml:"batcher"` + Proposer common.Address `json:"proposer" toml:"proposer"` + Challenger common.Address `json:"challenger" toml:"challenger"` +} + +var ErrChainRoleZeroAddress = fmt.Errorf("ChainRole is set to zero address") +var ErrFeeVaultZeroAddress = fmt.Errorf("chain has a fee vault set to zero address") +var ErrNonStandardValue = fmt.Errorf("chain contains non-standard config value") +var ErrEip1559ZeroValue = fmt.Errorf("eip1559 param is set to zero value") + +func (c *ChainIntent) Check() error { + if c.ID == emptyHash { + return fmt.Errorf("id must be set") + } + + if err := c.Roles.CheckNoZeroAddresses(); err != nil { + return err + } + + if c.Eip1559DenominatorCanyon == 0 || + c.Eip1559Denominator == 0 || + c.Eip1559Elasticity == 0 { + return fmt.Errorf("%w: chainId=%s", ErrEip1559ZeroValue, c.ID) + } + if c.BaseFeeVaultRecipient == emptyAddress || + c.L1FeeVaultRecipient == emptyAddress || + c.SequencerFeeVaultRecipient == emptyAddress { + return fmt.Errorf("%w: chainId=%s", ErrFeeVaultZeroAddress, c.ID) + } + + if c.DangerousAltDAConfig.UseAltDA { + return c.DangerousAltDAConfig.Check(nil) + } + + return nil +} + +// Returns an error if any fields in ChainRoles is set to common.Address{} +func (cr *ChainRoles) CheckNoZeroAddresses() error { + val := reflect.ValueOf(*cr) + typ := reflect.TypeOf(*cr) + + // Iterate through all the fields + for i := 0; i < val.NumField(); i++ { + fieldValue := val.Field(i) + fieldName := typ.Field(i).Name + + if fieldValue.Interface() == (common.Address{}) { + return fmt.Errorf("%w: %s", ErrChainRoleZeroAddress, fieldName) + } + } + + return nil +} diff --git a/op-deployer/pkg/deployer/state/intent.go b/op-deployer/pkg/deployer/state/intent.go index 86e6cfc1ed4..067ea5bead6 100644 --- a/op-deployer/pkg/deployer/state/intent.go +++ b/op-deployer/pkg/deployer/state/intent.go @@ -4,8 +4,8 @@ import ( "errors" "fmt" "math/big" - - "github.com/ethereum-optimism/optimism/op-chain-ops/genesis" + "net/url" + "reflect" "github.com/ethereum-optimism/optimism/op-deployer/pkg/deployer/artifacts" @@ -32,63 +32,197 @@ func (d DeploymentStrategy) Check() error { } } +type IntentConfigType string + +const ( + IntentConfigTypeStandard IntentConfigType = "standard" + IntentConfigTypeCustom IntentConfigType = "custom" + IntentConfigTypeStrict IntentConfigType = "strict" + IntentConfigTypeStandardOverrides IntentConfigType = "standard-overrides" + IntentConfigTypeStrictOverrides IntentConfigType = "strict-overrides" +) + var emptyAddress common.Address +var emptyHash common.Hash type Intent struct { - DeploymentStrategy DeploymentStrategy `json:"deploymentStrategy" toml:"deploymentStrategy"` + DeploymentStrategy DeploymentStrategy `json:"deploymentStrategy" toml:"deploymentStrategy"` + ConfigType IntentConfigType `json:"configType" toml:"configType"` + L1ChainID uint64 `json:"l1ChainID" toml:"l1ChainID"` + SuperchainRoles *SuperchainRoles `json:"superchainRoles" toml:"superchainRoles,omitempty"` + FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"` + UseInterop bool `json:"useInterop" toml:"useInterop"` + L1ContractsLocator *artifacts.Locator `json:"l1ContractsLocator" toml:"l1ContractsLocator"` + L2ContractsLocator *artifacts.Locator `json:"l2ContractsLocator" toml:"l2ContractsLocator"` + Chains []*ChainIntent `json:"chains" toml:"chains"` + GlobalDeployOverrides map[string]any `json:"globalDeployOverrides" toml:"globalDeployOverrides"` +} - L1ChainID uint64 `json:"l1ChainID" toml:"l1ChainID"` +type SuperchainRoles struct { + ProxyAdminOwner common.Address `json:"proxyAdminOwner" toml:"proxyAdminOwner"` + ProtocolVersionsOwner common.Address `json:"protocolVersionsOwner" toml:"protocolVersionsOwner"` + Guardian common.Address `json:"guardian" toml:"guardian"` +} - SuperchainRoles *SuperchainRoles `json:"superchainRoles" toml:"superchainRoles,omitempty"` +var ErrSuperchainRoleZeroAddress = errors.New("SuperchainRole is set to zero address") +var ErrL1ContractsLocatorUndefined = errors.New("L1ContractsLocator undefined") +var ErrL2ContractsLocatorUndefined = errors.New("L2ContractsLocator undefined") - FundDevAccounts bool `json:"fundDevAccounts" toml:"fundDevAccounts"` +func (s *SuperchainRoles) CheckNoZeroAddresses() error { + val := reflect.ValueOf(*s) + typ := reflect.TypeOf(*s) - UseInterop bool `json:"useInterop" toml:"useInterop"` + // Iterate through all the fields + for i := 0; i < val.NumField(); i++ { + fieldValue := val.Field(i) + fieldName := typ.Field(i).Name - L1ContractsLocator *artifacts.Locator `json:"l1ContractsLocator" toml:"l1ContractsLocator"` + if fieldValue.Interface() == (common.Address{}) { + return fmt.Errorf("%w: %s", ErrSuperchainRoleZeroAddress, fieldName) + } + } + return nil +} - L2ContractsLocator *artifacts.Locator `json:"l2ContractsLocator" toml:"l2ContractsLocator"` +func (c *Intent) L1ChainIDBig() *big.Int { + return big.NewInt(int64(c.L1ChainID)) +} + +func (c *Intent) validateCustomConfig() error { + if c.L1ContractsLocator == nil || + (c.L1ContractsLocator.Tag == "" && c.L1ContractsLocator.URL == &url.URL{}) { + return ErrL1ContractsLocatorUndefined + } + if c.L2ContractsLocator == nil || + (c.L2ContractsLocator.Tag == "" && c.L2ContractsLocator.URL == &url.URL{}) { + return ErrL2ContractsLocatorUndefined + } + + if err := c.SuperchainRoles.CheckNoZeroAddresses(); err != nil { + return err + } - Chains []*ChainIntent `json:"chains" toml:"chains"` + if len(c.Chains) == 0 { + return errors.New("must define at least one l2 chain") + } - GlobalDeployOverrides map[string]any `json:"globalDeployOverrides" toml:"globalDeployOverrides"` + for _, chain := range c.Chains { + if err := chain.Check(); err != nil { + return err + } + } + + return nil } -func (c *Intent) L1ChainIDBig() *big.Int { - return big.NewInt(int64(c.L1ChainID)) +func (c *Intent) validateStrictConfig() error { + if err := c.validateStandardValues(); err != nil { + return err + } + + challenger, _ := standard.ChallengerAddressFor(c.L1ChainID) + l1ProxyAdminOwner, _ := standard.L1ProxyAdminOwner(c.L1ChainID) + for chainIndex := range c.Chains { + if c.Chains[chainIndex].Roles.Challenger != challenger { + return fmt.Errorf("invalid challenger address for chain: %s", c.Chains[chainIndex].ID) + } + if c.Chains[chainIndex].Roles.L1ProxyAdminOwner != l1ProxyAdminOwner { + return fmt.Errorf("invalid l1ProxyAdminOwner address for chain: %s", c.Chains[chainIndex].ID) + } + } + + return nil } -func (c *Intent) Check() error { - if c.DeploymentStrategy != DeploymentStrategyLive && c.DeploymentStrategy != DeploymentStrategyGenesis { - return fmt.Errorf("deploymentStrategy must be 'live' or 'local'") +// Ensures the following: +// 1. no zero-values for non-standard fields (user should have populated these) +// 2. no non-standard values for standard fields (user should not have changed these) +func (c *Intent) validateStandardValues() error { + if err := c.checkL1Prod(); err != nil { + return err + } + if err := c.checkL2Prod(); err != nil { + return err + } + + standardSuperchainRoles, err := getStandardSuperchainRoles(c.L1ChainID) + if err != nil { + return fmt.Errorf("error getting standard superchain roles: %w", err) + } + if *c.SuperchainRoles != *standardSuperchainRoles { + return fmt.Errorf("SuperchainRoles does not match standard value") + } + + for _, chain := range c.Chains { + if err := chain.Check(); err != nil { + return err + } + if chain.Eip1559DenominatorCanyon != standard.Eip1559DenominatorCanyon || + chain.Eip1559Denominator != standard.Eip1559Denominator || + chain.Eip1559Elasticity != standard.Eip1559Elasticity { + return fmt.Errorf("%w: chainId=%s", ErrNonStandardValue, chain.ID) + } + } + + return nil +} + +func getStandardSuperchainRoles(l1ChainId uint64) (*SuperchainRoles, error) { + superCfg, err := standard.SuperchainFor(l1ChainId) + if err != nil { + return nil, fmt.Errorf("error getting superchain config: %w", err) } + proxyAdminOwner, err := standard.L1ProxyAdminOwner(l1ChainId) + if err != nil { + return nil, fmt.Errorf("error getting L1ProxyAdminOwner: %w", err) + } + guardian, err := standard.GuardianAddressFor(l1ChainId) + if err != nil { + return nil, fmt.Errorf("error getting guardian address: %w", err) + } + + superchainRoles := &SuperchainRoles{ + ProxyAdminOwner: proxyAdminOwner, + ProtocolVersionsOwner: common.Address(*superCfg.Config.ProtocolVersionsAddr), + Guardian: guardian, + } + + return superchainRoles, nil +} + +func (c *Intent) Check() error { if c.L1ChainID == 0 { - return fmt.Errorf("l1ChainID must be set") + return fmt.Errorf("l1ChainID cannot be 0") + } + + if err := c.DeploymentStrategy.Check(); err != nil { + return err } if c.L1ContractsLocator == nil { - return errors.New("l1ContractsLocator must be set") + return ErrL1ContractsLocatorUndefined } if c.L2ContractsLocator == nil { - return errors.New("l2ContractsLocator must be set") + return ErrL2ContractsLocatorUndefined } var err error - if c.L1ContractsLocator.IsTag() { - err = c.checkL1Prod() - } else { - err = c.checkL1Dev() + switch c.ConfigType { + case IntentConfigTypeStandard: + err = c.validateStandardValues() + case IntentConfigTypeCustom: + err = c.validateCustomConfig() + case IntentConfigTypeStrict: + err = c.validateStrictConfig() + case IntentConfigTypeStandardOverrides, IntentConfigTypeStrictOverrides: + err = c.validateCustomConfig() + default: + return fmt.Errorf("intent-config-type unsupported: %s", c.ConfigType) } if err != nil { - return err - } - - if c.L2ContractsLocator.IsTag() { - if err := c.checkL2Prod(); err != nil { - return err - } + return fmt.Errorf("failed to validate intent-config-type=%s: %w", c.ConfigType, err) } return nil @@ -121,100 +255,113 @@ func (c *Intent) checkL1Prod() error { return nil } -func (c *Intent) checkL1Dev() error { - if c.SuperchainRoles.ProxyAdminOwner == emptyAddress { - return fmt.Errorf("proxyAdminOwner must be set") - } - - if c.SuperchainRoles.ProtocolVersionsOwner == emptyAddress { - c.SuperchainRoles.ProtocolVersionsOwner = c.SuperchainRoles.ProxyAdminOwner - } - - if c.SuperchainRoles.Guardian == emptyAddress { - c.SuperchainRoles.Guardian = c.SuperchainRoles.ProxyAdminOwner - } - - return nil -} - func (c *Intent) checkL2Prod() error { _, err := standard.ArtifactsURLForTag(c.L2ContractsLocator.Tag) return err } -type SuperchainRoles struct { - ProxyAdminOwner common.Address `json:"proxyAdminOwner" toml:"proxyAdminOwner"` - - ProtocolVersionsOwner common.Address `json:"protocolVersionsOwner" toml:"protocolVersionsOwner"` - - Guardian common.Address `json:"guardian" toml:"guardian"` -} - -type ChainIntent struct { - ID common.Hash `json:"id" toml:"id"` - - BaseFeeVaultRecipient common.Address `json:"baseFeeVaultRecipient" toml:"baseFeeVaultRecipient"` +func NewIntent(configType IntentConfigType, deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + switch configType { + case IntentConfigTypeCustom: + return NewIntentCustom(deploymentStrategy, l1ChainId, l2ChainIds) - L1FeeVaultRecipient common.Address `json:"l1FeeVaultRecipient" toml:"l1FeeVaultRecipient"` + case IntentConfigTypeStandard: + return NewIntentStandard(deploymentStrategy, l1ChainId, l2ChainIds) - SequencerFeeVaultRecipient common.Address `json:"sequencerFeeVaultRecipient" toml:"sequencerFeeVaultRecipient"` + case IntentConfigTypeStandardOverrides: + return NewIntentStandardOverrides(deploymentStrategy, l1ChainId, l2ChainIds) - Eip1559Denominator uint64 `json:"eip1559Denominator" toml:"eip1559Denominator"` + case IntentConfigTypeStrict: + return NewIntentStrict(deploymentStrategy, l1ChainId, l2ChainIds) - Eip1559Elasticity uint64 `json:"eip1559Elasticity" toml:"eip1559Elasticity"` + case IntentConfigTypeStrictOverrides: + return NewIntentStrictOverrides(deploymentStrategy, l1ChainId, l2ChainIds) - Roles ChainRoles `json:"roles" toml:"roles"` - - DeployOverrides map[string]any `json:"deployOverrides" toml:"deployOverrides"` - - DangerousAltDAConfig genesis.AltDADeployConfig `json:"dangerousAltDAConfig,omitempty" toml:"dangerousAltDAConfig,omitempty"` + default: + return Intent{}, fmt.Errorf("intent config type not supported") + } } -type ChainRoles struct { - L1ProxyAdminOwner common.Address `json:"l1ProxyAdminOwner" toml:"l1ProxyAdminOwner"` - - L2ProxyAdminOwner common.Address `json:"l2ProxyAdminOwner" toml:"l2ProxyAdminOwner"` - - SystemConfigOwner common.Address `json:"systemConfigOwner" toml:"systemConfigOwner"` - - UnsafeBlockSigner common.Address `json:"unsafeBlockSigner" toml:"unsafeBlockSigner"` - - Batcher common.Address `json:"batcher" toml:"batcher"` - - Proposer common.Address `json:"proposer" toml:"proposer"` +// Sets all Intent fields to their zero value with the expectation that the +// user will populate the values before running 'apply' +func NewIntentCustom(deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + intent := Intent{ + DeploymentStrategy: deploymentStrategy, + ConfigType: IntentConfigTypeCustom, + L1ChainID: l1ChainId, + L1ContractsLocator: &artifacts.Locator{URL: &url.URL{}}, + L2ContractsLocator: &artifacts.Locator{URL: &url.URL{}}, + SuperchainRoles: &SuperchainRoles{}, + } - Challenger common.Address `json:"challenger" toml:"challenger"` + for _, l2ChainID := range l2ChainIds { + intent.Chains = append(intent.Chains, &ChainIntent{ + ID: l2ChainID, + }) + } + return intent, nil } -func (c *ChainIntent) Check() error { - var emptyHash common.Hash - if c.ID == emptyHash { - return fmt.Errorf("id must be set") +func NewIntentStandard(deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + intent := Intent{ + DeploymentStrategy: deploymentStrategy, + ConfigType: IntentConfigTypeStandard, + L1ChainID: l1ChainId, + L1ContractsLocator: artifacts.DefaultL1ContractsLocator, + L2ContractsLocator: artifacts.DefaultL2ContractsLocator, } - if c.Roles.L1ProxyAdminOwner == emptyAddress { - return fmt.Errorf("proxyAdminOwner must be set") + superchainRoles, err := getStandardSuperchainRoles(l1ChainId) + if err != nil { + return Intent{}, fmt.Errorf("error getting standard superchain roles: %w", err) } - - if c.Roles.SystemConfigOwner == emptyAddress { - c.Roles.SystemConfigOwner = c.Roles.L1ProxyAdminOwner + intent.SuperchainRoles = superchainRoles + + for _, l2ChainID := range l2ChainIds { + intent.Chains = append(intent.Chains, &ChainIntent{ + ID: l2ChainID, + Eip1559DenominatorCanyon: standard.Eip1559DenominatorCanyon, + Eip1559Denominator: standard.Eip1559Denominator, + Eip1559Elasticity: standard.Eip1559Elasticity, + }) } + return intent, nil +} - if c.Roles.L2ProxyAdminOwner == emptyAddress { - return fmt.Errorf("l2ProxyAdminOwner must be set") +func NewIntentStandardOverrides(deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + intent, err := NewIntentStandard(deploymentStrategy, l1ChainId, l2ChainIds) + if err != nil { + return Intent{}, err } + intent.ConfigType = IntentConfigTypeStandardOverrides + + return intent, nil +} - if c.Roles.UnsafeBlockSigner == emptyAddress { - return fmt.Errorf("unsafeBlockSigner must be set") +// Same as NewIntentStandard, but also sets l2 Challenger and L1ProxyAdminOwner +// addresses to standard values +func NewIntentStrict(deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + intent, err := NewIntentStandard(deploymentStrategy, l1ChainId, l2ChainIds) + if err != nil { + return Intent{}, err } + intent.ConfigType = IntentConfigTypeStrict - if c.Roles.Batcher == emptyAddress { - return fmt.Errorf("batcher must be set") + challenger, _ := standard.ChallengerAddressFor(l1ChainId) + l1ProxyAdminOwner, _ := standard.ManagerOwnerAddrFor(l1ChainId) + for chainIndex := range intent.Chains { + intent.Chains[chainIndex].Roles.Challenger = challenger + intent.Chains[chainIndex].Roles.L1ProxyAdminOwner = l1ProxyAdminOwner } + return intent, nil +} - if c.DangerousAltDAConfig.UseAltDA { - return c.DangerousAltDAConfig.Check(nil) +func NewIntentStrictOverrides(deploymentStrategy DeploymentStrategy, l1ChainId uint64, l2ChainIds []common.Hash) (Intent, error) { + intent, err := NewIntentStrict(deploymentStrategy, l1ChainId, l2ChainIds) + if err != nil { + return Intent{}, err } + intent.ConfigType = IntentConfigTypeStrictOverrides - return nil + return intent, nil } diff --git a/op-deployer/pkg/deployer/state/intent_test.go b/op-deployer/pkg/deployer/state/intent_test.go new file mode 100644 index 00000000000..1d8c12375f8 --- /dev/null +++ b/op-deployer/pkg/deployer/state/intent_test.go @@ -0,0 +1,89 @@ +package state + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" +) + +func TestValidateStandardValues(t *testing.T) { + intent, err := NewIntentStandard(DeploymentStrategyLive, 1, []common.Hash{common.HexToHash("0x336")}) + require.NoError(t, err) + + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrChainRoleZeroAddress) + + setChainRoles(&intent) + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrFeeVaultZeroAddress) + + setFeeAddresses(&intent) + err = intent.Check() + require.NoError(t, err) + + intent.Chains[0].Eip1559Denominator = 3 // set to non-standard value + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrNonStandardValue) +} + +func TestValidateCustomValues(t *testing.T) { + intent, err := NewIntentCustom(DeploymentStrategyLive, 1, []common.Hash{common.HexToHash("0x336")}) + require.NoError(t, err) + + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrSuperchainRoleZeroAddress) + + setSuperchainRoles(&intent) + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrChainRoleZeroAddress) + + setChainRoles(&intent) + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrEip1559ZeroValue) + + setEip1559Params(&intent) + err = intent.Check() + require.Error(t, err) + require.ErrorIs(t, err, ErrFeeVaultZeroAddress) + + setFeeAddresses(&intent) + err = intent.Check() + require.NoError(t, err) +} + +func setSuperchainRoles(intent *Intent) { + intent.SuperchainRoles = &SuperchainRoles{ + ProxyAdminOwner: common.HexToAddress("0xa"), + ProtocolVersionsOwner: common.HexToAddress("0xb"), + Guardian: common.HexToAddress("0xc"), + } +} + +func setEip1559Params(intent *Intent) { + intent.Chains[0].Eip1559Denominator = 5000 + intent.Chains[0].Eip1559DenominatorCanyon = 5000 + intent.Chains[0].Eip1559Elasticity = 5000 +} + +func setChainRoles(intent *Intent) { + intent.Chains[0].Roles.L1ProxyAdminOwner = common.HexToAddress("0x01") + intent.Chains[0].Roles.L2ProxyAdminOwner = common.HexToAddress("0x02") + intent.Chains[0].Roles.SystemConfigOwner = common.HexToAddress("0x03") + intent.Chains[0].Roles.UnsafeBlockSigner = common.HexToAddress("0x04") + intent.Chains[0].Roles.Batcher = common.HexToAddress("0x05") + intent.Chains[0].Roles.Proposer = common.HexToAddress("0x06") + intent.Chains[0].Roles.Challenger = common.HexToAddress("0x07") +} + +func setFeeAddresses(intent *Intent) { + intent.Chains[0].BaseFeeVaultRecipient = common.HexToAddress("0x08") + intent.Chains[0].L1FeeVaultRecipient = common.HexToAddress("0x09") + intent.Chains[0].SequencerFeeVaultRecipient = common.HexToAddress("0x0A") +} diff --git a/op-dispute-mon/mon/extract/head_enricher.go b/op-dispute-mon/mon/extract/head_enricher.go index 943e648f771..4162c9e896d 100644 --- a/op-dispute-mon/mon/extract/head_enricher.go +++ b/op-dispute-mon/mon/extract/head_enricher.go @@ -5,15 +5,15 @@ import ( "fmt" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" ) var _ Enricher = (*L1HeadBlockNumEnricher)(nil) type BlockFetcher interface { - HeaderByHash(ctx context.Context, block common.Hash) (*types.Header, error) + L1BlockRefByHash(ctx context.Context, block common.Hash) (eth.L1BlockRef, error) } type L1HeadBlockNumEnricher struct { @@ -25,10 +25,10 @@ func NewL1HeadBlockNumEnricher(client BlockFetcher) *L1HeadBlockNumEnricher { } func (e *L1HeadBlockNumEnricher) Enrich(ctx context.Context, _ rpcblock.Block, _ GameCaller, game *monTypes.EnrichedGameData) error { - header, err := e.client.HeaderByHash(ctx, game.L1Head) + header, err := e.client.L1BlockRefByHash(ctx, game.L1Head) if err != nil { return fmt.Errorf("failed to retrieve header for L1 head block %v: %w", game.L1Head, err) } - game.L1HeadNum = header.Number.Uint64() + game.L1HeadNum = header.Number return nil } diff --git a/op-dispute-mon/mon/extract/head_enricher_test.go b/op-dispute-mon/mon/extract/head_enricher_test.go index c0cb03b86b7..3c54c09516a 100644 --- a/op-dispute-mon/mon/extract/head_enricher_test.go +++ b/op-dispute-mon/mon/extract/head_enricher_test.go @@ -3,13 +3,12 @@ package extract import ( "context" "errors" - "math/big" "testing" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/sources/batching/rpcblock" "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" ) @@ -39,11 +38,11 @@ type stubBlockFetcher struct { err error } -func (s *stubBlockFetcher) HeaderByHash(_ context.Context, _ common.Hash) (*gethTypes.Header, error) { +func (s *stubBlockFetcher) L1BlockRefByHash(_ context.Context, _ common.Hash) (eth.L1BlockRef, error) { if s.err != nil { - return nil, s.err + return eth.L1BlockRef{}, s.err } - return &gethTypes.Header{ - Number: new(big.Int).SetUint64(s.num), + return eth.L1BlockRef{ + Number: s.num, }, nil } diff --git a/op-dispute-mon/mon/monitor.go b/op-dispute-mon/mon/monitor.go index 5f7764adccb..3fb14525e24 100644 --- a/op-dispute-mon/mon/monitor.go +++ b/op-dispute-mon/mon/monitor.go @@ -3,11 +3,11 @@ package mon import ( "context" "fmt" - "math/big" "time" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" "github.com/ethereum-optimism/optimism/op-service/clock" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -15,8 +15,7 @@ import ( type ForecastResolution func(games []*types.EnrichedGameData, ignoredCount, failedCount int) type Monitor func(games []*types.EnrichedGameData) -type BlockHashFetcher func(ctx context.Context, number *big.Int) (common.Hash, error) -type BlockNumberFetcher func(ctx context.Context) (uint64, error) +type HeadBlockFetcher func(ctx context.Context) (eth.L1BlockRef, error) type Extract func(ctx context.Context, blockHash common.Hash, minTimestamp uint64) ([]*types.EnrichedGameData, int, int, error) type MonitorMetrics interface { @@ -35,11 +34,10 @@ type gameMonitor struct { gameWindow time.Duration monitorInterval time.Duration - forecast ForecastResolution - monitors []Monitor - extract Extract - fetchBlockHash BlockHashFetcher - fetchBlockNumber BlockNumberFetcher + forecast ForecastResolution + monitors []Monitor + extract Extract + fetchHeadBlock HeadBlockFetcher } func newGameMonitor( @@ -49,40 +47,34 @@ func newGameMonitor( metrics MonitorMetrics, monitorInterval time.Duration, gameWindow time.Duration, - fetchBlockHash BlockHashFetcher, - fetchBlockNumber BlockNumberFetcher, + fetchHeadBlock HeadBlockFetcher, extract Extract, forecast ForecastResolution, monitors ...Monitor) *gameMonitor { return &gameMonitor{ - logger: logger, - clock: cl, - ctx: ctx, - done: make(chan struct{}), - metrics: metrics, - monitorInterval: monitorInterval, - gameWindow: gameWindow, - forecast: forecast, - monitors: monitors, - extract: extract, - fetchBlockNumber: fetchBlockNumber, - fetchBlockHash: fetchBlockHash, + logger: logger, + clock: cl, + ctx: ctx, + done: make(chan struct{}), + metrics: metrics, + monitorInterval: monitorInterval, + gameWindow: gameWindow, + forecast: forecast, + monitors: monitors, + extract: extract, + fetchHeadBlock: fetchHeadBlock, } } func (m *gameMonitor) monitorGames() error { start := m.clock.Now() - blockNumber, err := m.fetchBlockNumber(m.ctx) + headBlock, err := m.fetchHeadBlock(m.ctx) if err != nil { return fmt.Errorf("failed to fetch block number: %w", err) } - m.logger.Debug("Fetched block number", "blockNumber", blockNumber) - blockHash, err := m.fetchBlockHash(context.Background(), new(big.Int).SetUint64(blockNumber)) - if err != nil { - return fmt.Errorf("failed to fetch block hash: %w", err) - } + m.logger.Debug("Fetched current head block", "block", headBlock) minGameTimestamp := clock.MinCheckedTimestamp(m.clock, m.gameWindow) - enrichedGames, ignored, failed, err := m.extract(m.ctx, blockHash, minGameTimestamp) + enrichedGames, ignored, failed, err := m.extract(m.ctx, headBlock.Hash, minGameTimestamp) if err != nil { return fmt.Errorf("failed to load games: %w", err) } @@ -92,7 +84,13 @@ func (m *gameMonitor) monitorGames() error { } timeTaken := m.clock.Since(start) m.metrics.RecordMonitorDuration(timeTaken) - m.logger.Info("Completed monitoring update", "blockNumber", blockNumber, "blockHash", blockHash, "duration", timeTaken, "games", len(enrichedGames), "ignored", ignored, "failed", failed) + m.logger.Info("Completed monitoring update", + "blockNumber", headBlock.Number, + "blockHash", headBlock.Hash, + "duration", timeTaken, + "games", len(enrichedGames), + "ignored", ignored, + "failed", failed) return nil } diff --git a/op-dispute-mon/mon/monitor_test.go b/op-dispute-mon/mon/monitor_test.go index 1181c57b4ec..2161e6af14c 100644 --- a/op-dispute-mon/mon/monitor_test.go +++ b/op-dispute-mon/mon/monitor_test.go @@ -3,7 +3,6 @@ package mon import ( "context" "errors" - "math/big" "testing" "time" @@ -11,6 +10,7 @@ import ( "github.com/ethereum-optimism/optimism/op-dispute-mon/metrics" monTypes "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" "github.com/ethereum-optimism/optimism/op-service/clock" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum-optimism/optimism/op-service/testlog" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -24,21 +24,11 @@ var ( func TestMonitor_MonitorGames(t *testing.T) { t.Parallel() - t.Run("FailedFetchBlocknumber", func(t *testing.T) { + t.Run("FailedFetchHeadBlock", func(t *testing.T) { monitor, _, _, _ := setupMonitorTest(t) boom := errors.New("boom") - monitor.fetchBlockNumber = func(ctx context.Context) (uint64, error) { - return 0, boom - } - err := monitor.monitorGames() - require.ErrorIs(t, err, boom) - }) - - t.Run("FailedFetchBlockHash", func(t *testing.T) { - monitor, _, _, _ := setupMonitorTest(t) - boom := errors.New("boom") - monitor.fetchBlockHash = func(ctx context.Context, number *big.Int) (common.Hash, error) { - return common.Hash{}, boom + monitor.fetchHeadBlock = func(ctx context.Context) (eth.L1BlockRef, error) { + return eth.L1BlockRef{}, boom } err := monitor.monitorGames() require.ErrorIs(t, err, boom) @@ -108,11 +98,8 @@ func newEnrichedGameData(proxy common.Address, timestamp uint64) *monTypes.Enric func setupMonitorTest(t *testing.T) (*gameMonitor, *mockExtractor, *mockForecast, []*mockMonitor) { logger := testlog.Logger(t, log.LvlDebug) - fetchBlockNum := func(ctx context.Context) (uint64, error) { - return 1, nil - } - fetchBlockHash := func(ctx context.Context, number *big.Int) (common.Hash, error) { - return common.Hash{}, nil + fetchHeadBlock := func(ctx context.Context) (eth.L1BlockRef, error) { + return eth.L1BlockRef{Number: 1, Hash: common.Hash{0xaa}}, nil } monitorInterval := 100 * time.Millisecond cl := clock.NewAdvancingClock(10 * time.Millisecond) @@ -122,7 +109,7 @@ func setupMonitorTest(t *testing.T) (*gameMonitor, *mockExtractor, *mockForecast monitor1 := &mockMonitor{} monitor2 := &mockMonitor{} monitor3 := &mockMonitor{} - monitor := newGameMonitor(context.Background(), logger, cl, metrics.NoopMetrics, monitorInterval, 10*time.Second, fetchBlockHash, fetchBlockNum, + monitor := newGameMonitor(context.Background(), logger, cl, metrics.NoopMetrics, monitorInterval, 10*time.Second, fetchHeadBlock, extractor.Extract, forecast.Forecast, monitor1.Check, monitor2.Check, monitor3.Check) return monitor, extractor, forecast, []*mockMonitor{monitor1, monitor2, monitor3} } diff --git a/op-dispute-mon/mon/service.go b/op-dispute-mon/mon/service.go index 083e391b911..c44f082d3a8 100644 --- a/op-dispute-mon/mon/service.go +++ b/op-dispute-mon/mon/service.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" - "math/big" "sync/atomic" + "time" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/bonds" "github.com/ethereum-optimism/optimism/op-dispute-mon/mon/types" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/ethclient" + rpcclient "github.com/ethereum-optimism/optimism/op-service/client" + "github.com/ethereum-optimism/optimism/op-service/eth" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-dispute-mon/config" @@ -47,7 +47,9 @@ type Service struct { withdrawals *WithdrawalMonitor rollupClient *sources.RollupClient - l1Client *ethclient.Client + l1RPC rpcclient.RPC + l1Client *sources.L1Client + l1Caller *batching.MultiCaller pprofService *oppprof.Service metricsSrv *httputil.HTTPServer @@ -120,7 +122,7 @@ func (s *Service) initWithdrawalMonitor() { } func (s *Service) initGameCallerCreator() { - s.game = extract.NewGameCallerCreator(s.metrics, batching.NewMultiCaller(s.l1Client.Client(), batching.DefaultBatchSize)) + s.game = extract.NewGameCallerCreator(s.metrics, s.l1Caller) } func (s *Service) initExtractor(cfg *config.Config) { @@ -159,10 +161,20 @@ func (s *Service) initOutputRollupClient(ctx context.Context, cfg *config.Config } func (s *Service) initL1Client(ctx context.Context, cfg *config.Config) error { - l1Client, err := dial.DialEthClientWithTimeout(ctx, dial.DefaultDialTimeout, s.logger, cfg.L1EthRpc) + l1RPC, err := dial.DialRPCClientWithTimeout(ctx, dial.DefaultDialTimeout, s.logger, cfg.L1EthRpc) if err != nil { return fmt.Errorf("failed to dial L1: %w", err) } + s.l1RPC = rpcclient.NewBaseRPCClient(l1RPC, rpcclient.WithCallTimeout(30*time.Second)) + s.l1Caller = batching.NewMultiCaller(s.l1RPC, batching.DefaultBatchSize) + // The RPC is trusted because the majority of data comes from contract calls which are not verified even when the + // RPC is untrusted and also avoids needing to update op-dispute-mon for L1 hard forks that change the header. + // Note that receipts are never fetched so the RPCKind has no actual effect. + clCfg := sources.L1ClientSimpleConfig(true, sources.RPCKindAny, 100) + l1Client, err := sources.NewL1Client(s.l1RPC, s.logger, s.metrics, clCfg) + if err != nil { + return fmt.Errorf("failed to init l1 client: %w", err) + } s.l1Client = l1Client return nil } @@ -203,24 +215,18 @@ func (s *Service) initMetricsServer(cfg *opmetrics.CLIConfig) error { } func (s *Service) initFactoryContract(cfg *config.Config) error { - factoryContract := contracts.NewDisputeGameFactoryContract(s.metrics, cfg.GameFactoryAddress, - batching.NewMultiCaller(s.l1Client.Client(), batching.DefaultBatchSize)) + factoryContract := contracts.NewDisputeGameFactoryContract(s.metrics, cfg.GameFactoryAddress, s.l1Caller) s.factoryContract = factoryContract return nil } func (s *Service) initMonitor(ctx context.Context, cfg *config.Config) { - blockHashFetcher := func(ctx context.Context, blockNumber *big.Int) (common.Hash, error) { - block, err := s.l1Client.BlockByNumber(ctx, blockNumber) - if err != nil { - return common.Hash{}, fmt.Errorf("failed to fetch block by number: %w", err) - } - return block.Hash(), nil + headBlockFetcher := func(ctx context.Context) (eth.L1BlockRef, error) { + return s.l1Client.L1BlockRefByLabel(ctx, "latest") } l2ChallengesMonitor := NewL2ChallengesMonitor(s.logger, s.metrics) updateTimeMonitor := NewUpdateTimeMonitor(s.cl, s.metrics) - s.monitor = newGameMonitor(ctx, s.logger, s.cl, s.metrics, cfg.MonitorInterval, cfg.GameWindow, blockHashFetcher, - s.l1Client.BlockNumber, + s.monitor = newGameMonitor(ctx, s.logger, s.cl, s.metrics, cfg.MonitorInterval, cfg.GameWindow, headBlockFetcher, s.extractor.Extract, s.forecast.Forecast, s.bonds.CheckBonds, diff --git a/op-e2e/README.md b/op-e2e/README.md index d6f09619422..a52eaab6325 100644 --- a/op-e2e/README.md +++ b/op-e2e/README.md @@ -5,7 +5,6 @@ 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 cannon-prestate make devnet-allocs ``` diff --git a/op-e2e/actions/proofs/helpers/kona.go b/op-e2e/actions/proofs/helpers/kona.go index 9d34a98dda0..dc9b98cd809 100644 --- a/op-e2e/actions/proofs/helpers/kona.go +++ b/op-e2e/actions/proofs/helpers/kona.go @@ -16,15 +16,14 @@ import ( "github.com/stretchr/testify/require" ) -var konaHostPath, konaClientPath string +var konaHostPath string func init() { konaHostPath = os.Getenv("KONA_HOST_PATH") - konaClientPath = os.Getenv("KONA_CLIENT_PATH") } func IsKonaConfigured() bool { - return konaHostPath != "" && konaClientPath != "" + return konaHostPath != "" } func RunKonaNative( @@ -57,7 +56,7 @@ func RunKonaNative( L2Claim: fixtureInputs.L2Claim, L2BlockNumber: big.NewInt(int64(fixtureInputs.L2BlockNumber)), } - hostCmd, err := vm.NewNativeKonaExecutor(konaClientPath).OracleCommand(vmCfg, workDir, inputs) + hostCmd, err := vm.NewNativeKonaExecutor().OracleCommand(vmCfg, workDir, inputs) require.NoError(t, err) cmd := exec.Command(hostCmd[0], hostCmd[1:]...) diff --git a/op-e2e/e2eutils/retryproxy/proxy.go b/op-e2e/e2eutils/retryproxy/proxy.go new file mode 100644 index 00000000000..a74d5d8c869 --- /dev/null +++ b/op-e2e/e2eutils/retryproxy/proxy.go @@ -0,0 +1,160 @@ +package retryproxy + +import ( + "bytes" + "context" + "fmt" + "io" + "net" + "net/http" + "time" + + "github.com/ethereum-optimism/optimism/op-service/retry" + "github.com/ethereum/go-ethereum/log" +) + +var copyHeaders = []string{ + "Content-Type", +} + +type RetryProxy struct { + lgr log.Logger + upstream string + client *http.Client + strategy retry.Strategy + maxRetries int + srv *http.Server + listenPort int +} + +type Option func(*RetryProxy) + +func New(lgr log.Logger, upstream string, opts ...Option) *RetryProxy { + strategy := &retry.ExponentialStrategy{ + Min: 250 * time.Millisecond, + Max: 5 * time.Second, + MaxJitter: 250 * time.Millisecond, + } + + prox := &RetryProxy{ + lgr: lgr.New("module", "retryproxy"), + upstream: upstream, + client: &http.Client{}, + strategy: strategy, + maxRetries: 5, + } + + for _, opt := range opts { + opt(prox) + } + + return prox +} + +func (p *RetryProxy) Start() error { + errC := make(chan error, 1) + + go func() { + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + errC <- fmt.Errorf("failed to listen: %w", err) + } + + p.listenPort = ln.Addr().(*net.TCPAddr).Port + + p.srv = &http.Server{ + Addr: "127.0.0.1:0", + Handler: p, + } + errC <- p.srv.Serve(ln) + }() + + timer := time.NewTimer(100 * time.Millisecond) + select { + case err := <-errC: + return fmt.Errorf("failed to start server: %w", err) + case <-timer.C: + p.lgr.Info("server started", "port", p.listenPort) + return nil + } +} + +func (p *RetryProxy) Stop() error { + if p.srv == nil { + return nil + } + + return p.srv.Shutdown(context.Background()) +} + +func (p *RetryProxy) Endpoint() string { + return fmt.Sprintf("http://127.0.0.1:%d", p.listenPort) +} + +func (p *RetryProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + return + } + + defer r.Body.Close() + reqBody, err := io.ReadAll(r.Body) + if err != nil { + p.lgr.Error("failed to read request body", "err", err) + http.Error(w, "failed to read request body", http.StatusInternalServerError) + return + } + + //nolint:bodyClose + res, resBody, err := retry.Do2(r.Context(), p.maxRetries, p.strategy, func() (*http.Response, []byte, error) { + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + defer cancel() + res, err := p.doProxyReq(ctx, reqBody) + if err != nil { + p.lgr.Warn("failed to proxy request", "err", err) + return nil, nil, err + } + + defer res.Body.Close() + resBody, err := io.ReadAll(res.Body) + if err != nil { + p.lgr.Warn("failed to read response body", "err", err) + return nil, nil, err + } + + return res, resBody, nil + }) + if err != nil { + p.lgr.Error("permanently failed to proxy request", "err", err) + http.Error(w, "failed to proxy request", http.StatusBadGateway) + return + } + + for _, h := range copyHeaders { + w.Header().Set(h, res.Header.Get(h)) + } + + w.WriteHeader(http.StatusOK) + if _, err := io.Copy(w, bytes.NewReader(resBody)); err != nil { + p.lgr.Error("failed to copy response", "err", err) + http.Error(w, "failed to copy response", http.StatusInternalServerError) + } +} + +func (p *RetryProxy) doProxyReq(ctx context.Context, body []byte) (*http.Response, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.upstream, bytes.NewReader(body)) + if err != nil { + panic(fmt.Errorf("failed to create request: %w", err)) + } + res, err := p.client.Do(req) + if err != nil { + p.lgr.Warn("failed to proxy request", "err", err) + return nil, err + } + status := res.StatusCode + if status != 200 { + p.lgr.Warn("unexpected status code", "status", status) + return nil, fmt.Errorf("unexpected status code: %d", status) + } + return res, nil +} diff --git a/op-e2e/system/altda/concurrent_test.go b/op-e2e/system/altda/concurrent_test.go index 32506e5c4a1..e53c7f0f811 100644 --- a/op-e2e/system/altda/concurrent_test.go +++ b/op-e2e/system/altda/concurrent_test.go @@ -34,7 +34,9 @@ func TestBatcherConcurrentAltDARequests(t *testing.T) { cfg.DisableBatcher = true sys, err := cfg.Start(t) require.NoError(t, err, "Error starting up system") - defer sys.Close() + t.Cleanup(func() { + sys.Close() + }) // make every request take 5 seconds, such that only concurrent requests will be able to make progress fast enough sys.FakeAltDAServer.SetPutRequestLatency(5 * time.Second) diff --git a/op-e2e/system/proofs/build_helper.go b/op-e2e/system/proofs/build_helper.go index 42201279867..9bad76b9dcb 100644 --- a/op-e2e/system/proofs/build_helper.go +++ b/op-e2e/system/proofs/build_helper.go @@ -2,7 +2,9 @@ package proofs import ( "context" + "os" "os/exec" + "path/filepath" "strings" "testing" "time" @@ -12,6 +14,15 @@ import ( // BuildOpProgramClient builds the `op-program` client executable and returns the path to the resulting executable func BuildOpProgramClient(t *testing.T) string { + clientPath, err := filepath.Abs("../../../op-program/bin/op-program-client") + require.NoError(t, err) + + _, err = os.Stat(clientPath) + if err == nil { + return clientPath + } + require.ErrorIs(t, err, os.ErrNotExist) + t.Log("Building op-program-client") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() diff --git a/op-node/Makefile b/op-node/Makefile index c1d480d9b71..b63f489925a 100644 --- a/op-node/Makefile +++ b/op-node/Makefile @@ -1,65 +1,3 @@ -GITCOMMIT ?= $(shell git rev-parse HEAD) -GITDATE ?= $(shell git show -s --format='%ct') -# Find the github tag that points to this commit. If none are found, set the version string to "untagged" -# Prioritizes release tag, if one exists, over tags suffixed with "-rc" -VERSION ?= $(shell tags=$$(git tag --points-at $(GITCOMMIT) | grep '^op-node/' | sed 's/op-node\///' | sort -V); \ - preferred_tag=$$(echo "$$tags" | grep -v -- '-rc' | tail -n 1); \ - if [ -z "$$preferred_tag" ]; then \ - if [ -z "$$tags" ]; then \ - echo "untagged"; \ - else \ - echo "$$tags" | tail -n 1; \ - fi \ - else \ - echo $$preferred_tag; \ - fi) +DEPRECATED_TARGETS := op-node clean test fuzz generate-mocks readme -LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) -LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) -LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-node/version.Version=$(VERSION) -LDFLAGSSTRING +=-X github.com/ethereum-optimism/optimism/op-node/version.Meta=$(VERSION_META) -LDFLAGS := -ldflags "$(LDFLAGSSTRING)" - -# Use the old Apple linker to workaround broken xcode - https://github.com/golang/go/issues/65169 -ifeq ($(shell uname),Darwin) - FUZZLDFLAGS := -ldflags=-extldflags=-Wl,-ld_classic -endif - -op-node: - env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) CGO_ENABLED=0 go build -v $(LDFLAGS) -o ./bin/op-node ./cmd/main.go - -clean: - rm bin/op-node - -test: - go test -v ./... - -fuzz: - printf "%s\n" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoBedrockRoundTrip ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoEcotoneRoundTrip ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzL1InfoAgainstContract ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzUnmarshallLogEvent ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseFrames ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFrameUnmarshalBinary ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzBatchRoundTrip ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsRoundTrip ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsBadVersion ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataValid ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataBadLength ./rollup/derive" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzRejectCreateBlockBadTimestamp ./rollup/driver" \ - "go test $(FUZZLDFLAGS) -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDecodeDepositTxDataToL1Info ./rollup/driver" \ - | parallel -j 8 {} - -generate-mocks: - go generate ./... - -readme: - doctoc README.md - -.PHONY: \ - op-node \ - clean \ - test \ - fuzz \ - readme +include ../just/deprecated.mk diff --git a/op-node/justfile b/op-node/justfile new file mode 100644 index 00000000000..46aadcc84b3 --- /dev/null +++ b/op-node/justfile @@ -0,0 +1,49 @@ +import '../just/go.just' + +# Build ldflags string +_LDFLAGSSTRING := "'" + trim( + "-X main.GitCommit=" + GITCOMMIT + " " + \ + "-X main.GitDate=" + GITDATE + " " + \ + "-X github.com/ethereum-optimism/optimism/op-node/version.Version=" + VERSION + " " + \ + "-X github.com/ethereum-optimism/optimism/op-node/version.Meta=" + VERSION_META + " " + \ + "") + "'" + +BINARY := "./bin/op-node" + +# Build op-node binary +op-node: (go_build BINARY "./cmd" "-ldflags" _LDFLAGSSTRING) + +# Clean build artifacts +clean: + rm -f {{BINARY}} + +# Run tests +test: (go_test "./...") + +# Generate mocks +generate-mocks: (go_generate "./...") + +# Update readme +readme: + doctoc README.md + +[private] +node_fuzz_task FUZZ TIME='10s': (go_fuzz FUZZ TIME "./rollup/derive") + +# Run fuzz tests +fuzz: + printf "%s\n" \ + "FuzzL1InfoBedrockRoundTrip" \ + "FuzzL1InfoEcotoneRoundTrip" \ + "FuzzL1InfoAgainstContract" \ + "FuzzUnmarshallLogEvent" \ + "FuzzParseFrames" \ + "FuzzFrameUnmarshalBinary" \ + "FuzzBatchRoundTrip" \ + "FuzzDeriveDepositsRoundTrip" \ + "FuzzDeriveDepositsBadVersion" \ + "FuzzParseL1InfoDepositTxDataValid" \ + "FuzzParseL1InfoDepositTxDataBadLength" \ + "FuzzRejectCreateBlockBadTimestamp" \ + "FuzzDecodeDepositTxDataToL1Info" \ + | parallel -j {{PARALLEL_JOBS}} {{just_executable()}} node_fuzz_task {} diff --git a/op-node/node/node.go b/op-node/node/node.go index 2727552b819..8bf8040b465 100644 --- a/op-node/node/node.go +++ b/op-node/node/node.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + gosync "sync" "sync/atomic" "time" @@ -60,6 +61,7 @@ type OpNode struct { l2Source *sources.EngineClient // L2 Execution Engine RPC bindings server *rpcServer // RPC server hosting the rollup-node API p2pNode *p2p.NodeP2P // P2P node functionality + p2pMu gosync.Mutex // protects p2pNode p2pSigner p2p.Signer // p2p gossip application messages will be signed with this signer tracer Tracer // tracer to get events for testing/debugging runCfg *RuntimeConfig // runtime configurables @@ -434,8 +436,9 @@ func (n *OpNode) initRPCServer(cfg *Config) error { if err != nil { return err } - if n.p2pEnabled() { - server.EnableP2P(p2p.NewP2PAPIBackend(n.p2pNode, n.log, n.metrics)) + + if p2pNode := n.getP2PNodeIfEnabled(); p2pNode != nil { + server.EnableP2P(p2p.NewP2PAPIBackend(p2pNode, n.log, n.metrics)) } if cfg.RPC.EnableAdmin { server.EnableAdminAPI(NewAdminAPI(n.l2Driver, n.metrics, n.log)) @@ -487,6 +490,8 @@ func (n *OpNode) p2pEnabled() bool { } func (n *OpNode) initP2P(cfg *Config) (err error) { + n.p2pMu.Lock() + defer n.p2pMu.Unlock() if n.p2pNode != nil { panic("p2p node already initialized") } @@ -580,13 +585,13 @@ func (n *OpNode) PublishL2Payload(ctx context.Context, envelope *eth.ExecutionPa n.tracer.OnPublishL2Payload(ctx, envelope) // publish to p2p, if we are running p2p at all - if n.p2pEnabled() { + if p2pNode := n.getP2PNodeIfEnabled(); p2pNode != nil { payload := envelope.ExecutionPayload if n.p2pSigner == nil { return fmt.Errorf("node has no p2p signer, payload %s cannot be published", payload.ID()) } n.log.Info("Publishing signed execution payload on p2p", "id", payload.ID()) - return n.p2pNode.GossipOut().PublishL2Payload(ctx, envelope, n.p2pSigner) + return p2pNode.GossipOut().PublishL2Payload(ctx, envelope, n.p2pSigner) } // if p2p is not enabled then we just don't publish the payload return nil @@ -594,7 +599,7 @@ func (n *OpNode) PublishL2Payload(ctx context.Context, envelope *eth.ExecutionPa func (n *OpNode) OnUnsafeL2Payload(ctx context.Context, from peer.ID, envelope *eth.ExecutionPayloadEnvelope) error { // ignore if it's from ourselves - if n.p2pEnabled() && from == n.p2pNode.Host().ID() { + if p2pNode := n.getP2PNodeIfEnabled(); p2pNode != nil && from == p2pNode.Host().ID() { return nil } @@ -615,7 +620,7 @@ func (n *OpNode) OnUnsafeL2Payload(ctx context.Context, from peer.ID, envelope * } func (n *OpNode) RequestL2Range(ctx context.Context, start, end eth.L2BlockRef) error { - if n.p2pEnabled() && n.p2pNode.AltSyncEnabled() { + if p2pNode := n.getP2PNodeIfEnabled(); p2pNode != nil && p2pNode.AltSyncEnabled() { if unixTimeStale(start.Time, 12*time.Hour) { n.log.Debug( "ignoring request to sync L2 range, timestamp is too old for p2p", @@ -624,7 +629,7 @@ func (n *OpNode) RequestL2Range(ctx context.Context, start, end eth.L2BlockRef) "start_time", start.Time) return nil } - return n.p2pNode.RequestL2Range(ctx, start, end) + return p2pNode.RequestL2Range(ctx, start, end) } n.log.Debug("ignoring request to sync L2 range, no sync method available", "start", start, "end", end) return nil @@ -636,7 +641,7 @@ func unixTimeStale(timestamp uint64, duration time.Duration) bool { } func (n *OpNode) P2P() p2p.Node { - return n.p2pNode + return n.getP2PNodeIfEnabled() } func (n *OpNode) RuntimeConfig() ReadonlyRuntimeConfig { @@ -671,6 +676,8 @@ func (n *OpNode) Stop(ctx context.Context) error { result = multierror.Append(result, fmt.Errorf("error stopping sequencer: %w", err)) } } + + n.p2pMu.Lock() if n.p2pNode != nil { if err := n.p2pNode.Close(); err != nil { result = multierror.Append(result, fmt.Errorf("failed to close p2p node: %w", err)) @@ -678,6 +685,8 @@ func (n *OpNode) Stop(ctx context.Context) error { // Prevent further use of p2p. n.p2pNode = nil } + n.p2pMu.Unlock() + if n.p2pSigner != nil { if err := n.p2pSigner.Close(); err != nil { result = multierror.Append(result, fmt.Errorf("failed to close p2p signer: %w", err)) @@ -778,3 +787,13 @@ func (n *OpNode) HTTPEndpoint() string { } return fmt.Sprintf("http://%s", n.server.Addr().String()) } + +func (n *OpNode) getP2PNodeIfEnabled() *p2p.NodeP2P { + if !n.p2pEnabled() { + return nil + } + + n.p2pMu.Lock() + defer n.p2pMu.Unlock() + return n.p2pNode +} diff --git a/op-node/p2p/sync.go b/op-node/p2p/sync.go index c8777fe51f9..1b2cc570a2e 100644 --- a/op-node/p2p/sync.go +++ b/op-node/p2p/sync.go @@ -346,6 +346,9 @@ func (s *SyncClient) AddPeer(id peer.ID) { func (s *SyncClient) RemovePeer(id peer.ID) { s.peersLock.Lock() defer s.peersLock.Unlock() + if s.closingPeers { + return + } cancel, ok := s.peers[id] if !ok { s.log.Warn("cannot remove peer from sync duties, peer was not registered", "peer", id) diff --git a/op-node/rollup/engine/build_seal.go b/op-node/rollup/engine/build_seal.go index 25c1d95b8e7..cd3ca888827 100644 --- a/op-node/rollup/engine/build_seal.go +++ b/op-node/rollup/engine/build_seal.go @@ -113,14 +113,15 @@ func (eq *EngDeriver) onBuildSeal(ev BuildSealEvent) { depositCount, _ := lastDeposit(envelope.ExecutionPayload.Transactions) eq.metrics.CountSequencedTxsInBlock(txnCount, depositCount) - eq.log.Debug("Processed new L2 block", "l2_unsafe", ref, "l1_origin", ref.L1Origin, + eq.log.Debug("Built new L2 block", "l2_unsafe", ref, "l1_origin", ref.L1Origin, "txs", txnCount, "deposits", depositCount, "time", ref.Time, "seal_time", sealTime, "build_time", buildTime) eq.emitter.Emit(BuildSealedEvent{ - Concluding: ev.Concluding, - DerivedFrom: ev.DerivedFrom, - Info: ev.Info, - Envelope: envelope, - Ref: ref, + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, + BuildStarted: ev.BuildStarted, + Info: ev.Info, + Envelope: envelope, + Ref: ref, }) } diff --git a/op-node/rollup/engine/build_sealed.go b/op-node/rollup/engine/build_sealed.go index eb2680850a7..5ceff489ecc 100644 --- a/op-node/rollup/engine/build_sealed.go +++ b/op-node/rollup/engine/build_sealed.go @@ -1,6 +1,8 @@ package engine import ( + "time" + "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -10,7 +12,8 @@ type BuildSealedEvent struct { // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) Concluding bool // payload is promoted to pending-safe if non-zero - DerivedFrom eth.L1BlockRef + DerivedFrom eth.L1BlockRef + BuildStarted time.Time Info eth.PayloadInfo Envelope *eth.ExecutionPayloadEnvelope @@ -25,10 +28,11 @@ func (eq *EngDeriver) onBuildSealed(ev BuildSealedEvent) { // If a (pending) safe block, immediately process the block if ev.DerivedFrom != (eth.L1BlockRef{}) { eq.emitter.Emit(PayloadProcessEvent{ - Concluding: ev.Concluding, - DerivedFrom: ev.DerivedFrom, - Envelope: ev.Envelope, - Ref: ev.Ref, + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, + Envelope: ev.Envelope, + Ref: ev.Ref, + BuildStarted: ev.BuildStarted, }) } } diff --git a/op-node/rollup/engine/engine_controller.go b/op-node/rollup/engine/engine_controller.go index b088e382f07..907238e84a9 100644 --- a/op-node/rollup/engine/engine_controller.go +++ b/op-node/rollup/engine/engine_controller.go @@ -330,6 +330,7 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et } } // Insert the payload & then call FCU + newPayloadStart := time.Now() status, err := e.engine.NewPayload(ctx, envelope.ExecutionPayload, envelope.ParentBeaconBlockRoot) if err != nil { return derive.NewTemporaryError(fmt.Errorf("failed to update insert payload: %w", err)) @@ -342,6 +343,7 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et return derive.NewTemporaryError(fmt.Errorf("cannot process unsafe payload: new - %v; parent: %v; err: %w", payload.ID(), payload.ParentID(), eth.NewPayloadErr(payload, status))) } + newPayloadFinish := time.Now() // Mark the new payload as valid fc := eth.ForkchoiceState{ @@ -361,6 +363,7 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et } logFn := e.logSyncProgressMaybe() defer logFn() + fcu2Start := time.Now() fcRes, err := e.engine.ForkchoiceUpdate(ctx, &fc, nil) if err != nil { var rpcErr rpc.Error @@ -380,6 +383,7 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et return derive.NewTemporaryError(fmt.Errorf("cannot prepare unsafe chain for new payload: new - %v; parent: %v; err: %w", payload.ID(), payload.ParentID(), eth.ForkchoiceUpdateErr(fcRes.PayloadStatus))) } + fcu2Finish := time.Now() e.SetUnsafeHead(ref) e.needFCUCall = false e.emitter.Emit(UnsafeUpdateEvent{Ref: ref}) @@ -397,6 +401,16 @@ func (e *EngineController) InsertUnsafePayload(ctx context.Context, envelope *et }) } + totalTime := fcu2Finish.Sub(newPayloadStart) + e.log.Info("Inserted new L2 unsafe block (synchronous)", + "hash", envelope.ExecutionPayload.BlockHash, + "number", uint64(envelope.ExecutionPayload.BlockNumber), + "newpayload_time", common.PrettyDuration(newPayloadFinish.Sub(newPayloadStart)), + "fcu2_time", common.PrettyDuration(fcu2Finish.Sub(fcu2Start)), + "total_time", common.PrettyDuration(totalTime), + "mgas", float64(envelope.ExecutionPayload.GasUsed)/1000000, + "mgasps", float64(envelope.ExecutionPayload.GasUsed)*1000/float64(totalTime)) + return nil } diff --git a/op-node/rollup/engine/events.go b/op-node/rollup/engine/events.go index b6ba85cbcbe..fbc8b7b1db2 100644 --- a/op-node/rollup/engine/events.go +++ b/op-node/rollup/engine/events.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum-optimism/optimism/op-node/rollup" @@ -203,12 +204,67 @@ func (ev TryBackupUnsafeReorgEvent) String() string { return "try-backup-unsafe-reorg" } -type TryUpdateEngineEvent struct{} +type TryUpdateEngineEvent struct { + // These fields will be zero-value (BuildStarted,InsertStarted=time.Time{}, Envelope=nil) if + // this event is emitted outside of engineDeriver.onPayloadSuccess + BuildStarted time.Time + InsertStarted time.Time + Envelope *eth.ExecutionPayloadEnvelope +} func (ev TryUpdateEngineEvent) String() string { return "try-update-engine" } +// Checks for the existence of the Envelope field, which is only +// added by the PayloadSuccessEvent +func (ev TryUpdateEngineEvent) triggeredByPayloadSuccess() bool { + return ev.Envelope != nil +} + +// Returns key/value pairs that can be logged and are useful for plotting +// block build/insert time as a way to measure performance. +func (ev TryUpdateEngineEvent) getBlockProcessingMetrics() []interface{} { + fcuFinish := time.Now() + payload := ev.Envelope.ExecutionPayload + + logValues := []interface{}{ + "hash", payload.BlockHash, + "number", uint64(payload.BlockNumber), + "state_root", payload.StateRoot, + "timestamp", uint64(payload.Timestamp), + "parent", payload.ParentHash, + "prev_randao", payload.PrevRandao, + "fee_recipient", payload.FeeRecipient, + "txs", len(payload.Transactions), + } + + var totalTime time.Duration + var mgasps float64 + if !ev.BuildStarted.IsZero() { + totalTime = fcuFinish.Sub(ev.BuildStarted) + logValues = append(logValues, + "build_time", common.PrettyDuration(ev.InsertStarted.Sub(ev.BuildStarted)), + "insert_time", common.PrettyDuration(fcuFinish.Sub(ev.InsertStarted)), + ) + } else if !ev.InsertStarted.IsZero() { + totalTime = fcuFinish.Sub(ev.InsertStarted) + } + + // Avoid divide-by-zero for mgasps + if totalTime > 0 { + mgasps = float64(payload.GasUsed) * 1000 / float64(totalTime) + } + + logValues = append(logValues, + "total_time", common.PrettyDuration(totalTime), + "mgas", float64(payload.GasUsed)/1000000, + "mgasps", mgasps, + ) + + return logValues +} + type ForceEngineResetEvent struct { Unsafe, Safe, Finalized eth.L2BlockRef } @@ -322,6 +378,9 @@ func (d *EngDeriver) OnEvent(ev event.Event) bool { } else { d.emitter.Emit(rollup.CriticalErrorEvent{Err: fmt.Errorf("unexpected TryUpdateEngine error type: %w", err)}) } + } else if x.triggeredByPayloadSuccess() { + logValues := x.getBlockProcessingMetrics() + d.log.Info("Inserted new L2 unsafe block", logValues...) } case ProcessUnsafePayloadEvent: ref, err := derive.PayloadToBlockRef(d.cfg, x.Envelope.ExecutionPayload) diff --git a/op-node/rollup/engine/payload_process.go b/op-node/rollup/engine/payload_process.go index 62d7ded47f0..272fce3febc 100644 --- a/op-node/rollup/engine/payload_process.go +++ b/op-node/rollup/engine/payload_process.go @@ -3,6 +3,7 @@ package engine import ( "context" "fmt" + "time" "github.com/ethereum-optimism/optimism/op-node/rollup" "github.com/ethereum-optimism/optimism/op-service/eth" @@ -12,7 +13,8 @@ type PayloadProcessEvent struct { // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) Concluding bool // payload is promoted to pending-safe if non-zero - DerivedFrom eth.L1BlockRef + DerivedFrom eth.L1BlockRef + BuildStarted time.Time Envelope *eth.ExecutionPayloadEnvelope Ref eth.L2BlockRef @@ -26,6 +28,7 @@ func (eq *EngDeriver) onPayloadProcess(ev PayloadProcessEvent) { ctx, cancel := context.WithTimeout(eq.ctx, payloadProcessTimeout) defer cancel() + insertStart := time.Now() status, err := eq.ec.engine.NewPayload(ctx, ev.Envelope.ExecutionPayload, ev.Envelope.ParentBeaconBlockRoot) if err != nil { @@ -49,7 +52,14 @@ func (eq *EngDeriver) onPayloadProcess(ev PayloadProcessEvent) { }) return case eth.ExecutionValid: - eq.emitter.Emit(PayloadSuccessEvent(ev)) + eq.emitter.Emit(PayloadSuccessEvent{ + Concluding: ev.Concluding, + DerivedFrom: ev.DerivedFrom, + BuildStarted: ev.BuildStarted, + InsertStarted: insertStart, + Envelope: ev.Envelope, + Ref: ev.Ref, + }) return default: eq.emitter.Emit(rollup.EngineTemporaryErrorEvent{ diff --git a/op-node/rollup/engine/payload_success.go b/op-node/rollup/engine/payload_success.go index c00d8e81ea7..17d8b0163b5 100644 --- a/op-node/rollup/engine/payload_success.go +++ b/op-node/rollup/engine/payload_success.go @@ -1,6 +1,8 @@ package engine import ( + "time" + "github.com/ethereum-optimism/optimism/op-service/eth" ) @@ -8,7 +10,9 @@ type PayloadSuccessEvent struct { // if payload should be promoted to (local) safe (must also be pending safe, see DerivedFrom) Concluding bool // payload is promoted to pending-safe if non-zero - DerivedFrom eth.L1BlockRef + DerivedFrom eth.L1BlockRef + BuildStarted time.Time + InsertStarted time.Time Envelope *eth.ExecutionPayloadEnvelope Ref eth.L2BlockRef @@ -30,11 +34,9 @@ func (eq *EngDeriver) onPayloadSuccess(ev PayloadSuccessEvent) { }) } - payload := ev.Envelope.ExecutionPayload - eq.log.Info("Inserted block", "hash", payload.BlockHash, "number", uint64(payload.BlockNumber), - "state_root", payload.StateRoot, "timestamp", uint64(payload.Timestamp), "parent", payload.ParentHash, - "prev_randao", payload.PrevRandao, "fee_recipient", payload.FeeRecipient, - "txs", len(payload.Transactions), "concluding", ev.Concluding, "derived_from", ev.DerivedFrom) - - eq.emitter.Emit(TryUpdateEngineEvent{}) + eq.emitter.Emit(TryUpdateEngineEvent{ + BuildStarted: ev.BuildStarted, + InsertStarted: ev.InsertStarted, + Envelope: ev.Envelope, + }) } diff --git a/op-node/rollup/sequencing/sequencer.go b/op-node/rollup/sequencing/sequencer.go index e8b7033273e..2476b9b639f 100644 --- a/op-node/rollup/sequencing/sequencer.go +++ b/op-node/rollup/sequencing/sequencer.go @@ -282,10 +282,11 @@ func (d *Sequencer) onBuildSealed(x engine.BuildSealedEvent) { d.asyncGossip.Gossip(x.Envelope) // Now after having gossiped the block, try to put it in our own canonical chain d.emitter.Emit(engine.PayloadProcessEvent{ - Concluding: x.Concluding, - DerivedFrom: x.DerivedFrom, - Envelope: x.Envelope, - Ref: x.Ref, + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, + BuildStarted: x.BuildStarted, + Envelope: x.Envelope, + Ref: x.Ref, }) d.latest.Ref = x.Ref d.latestSealed = x.Ref diff --git a/op-node/rollup/sequencing/sequencer_chaos_test.go b/op-node/rollup/sequencing/sequencer_chaos_test.go index d5000fbed33..93ba254c1b5 100644 --- a/op-node/rollup/sequencing/sequencer_chaos_test.go +++ b/op-node/rollup/sequencing/sequencer_chaos_test.go @@ -216,7 +216,13 @@ func (c *ChaoticEngine) OnEvent(ev event.Event) bool { c.clockRandomIncrement(0, time.Second*3) } c.unsafe = x.Ref - c.emitter.Emit(engine.PayloadSuccessEvent(x)) + c.emitter.Emit(engine.PayloadSuccessEvent{ + Concluding: x.Concluding, + DerivedFrom: x.DerivedFrom, + BuildStarted: x.BuildStarted, + Envelope: x.Envelope, + Ref: x.Ref, + }) // With event delay, the engine would update and signal the new forkchoice. c.emitter.Emit(engine.ForkchoiceRequestEvent{}) } diff --git a/op-proposer/Makefile b/op-proposer/Makefile index 561d7d32f30..3a036e6d5d5 100644 --- a/op-proposer/Makefile +++ b/op-proposer/Makefile @@ -1,34 +1,3 @@ -GITCOMMIT ?= $(shell git rev-parse HEAD) -GITDATE ?= $(shell git show -s --format='%ct') -# Find the github tag that points to this commit. If none are found, set the version string to "untagged" -# Prioritizes release tag, if one exists, over tags suffixed with "-rc" -VERSION ?= $(shell tags=$$(git tag --points-at $(GITCOMMIT) | grep '^op-proposer/' | sed 's/op-proposer\///' | sort -V); \ - preferred_tag=$$(echo "$$tags" | grep -v -- '-rc' | tail -n 1); \ - if [ -z "$$preferred_tag" ]; then \ - if [ -z "$$tags" ]; then \ - echo "untagged"; \ - else \ - echo "$$tags" | tail -n 1; \ - fi \ - else \ - echo $$preferred_tag; \ - fi) +DEPRECATED_TARGETS := op-proposer clean test -LDFLAGSSTRING +=-X main.GitCommit=$(GITCOMMIT) -LDFLAGSSTRING +=-X main.GitDate=$(GITDATE) -LDFLAGSSTRING +=-X main.Version=$(VERSION) -LDFLAGS := -ldflags "$(LDFLAGSSTRING)" - -op-proposer: - env GO111MODULE=on GOOS=$(TARGETOS) GOARCH=$(TARGETARCH) CGO_ENABLED=0 go build -v $(LDFLAGS) -o ./bin/op-proposer ./cmd - -clean: - rm bin/op-proposer - -test: - go test -v ./... - -.PHONY: \ - clean \ - op-proposer \ - test +include ../just/deprecated.mk diff --git a/op-proposer/justfile b/op-proposer/justfile new file mode 100644 index 00000000000..08b1b7b7391 --- /dev/null +++ b/op-proposer/justfile @@ -0,0 +1,20 @@ +import '../just/go.just' + +# Build ldflags string +_LDFLAGSSTRING := "'" + trim( + "-X main.GitCommit=" + GITCOMMIT + " " + \ + "-X main.GitDate=" + GITDATE + " " + \ + "-X main.Version=" + VERSION + " " + \ + "") + "'" + +BINARY := "./bin/op-proposer" + +# Build op-proposer binary +op-proposer: (go_build BINARY "./cmd" "-ldflags" _LDFLAGSSTRING) + +# Clean build artifacts +clean: + rm -f {{BINARY}} + +# Run tests +test: (go_test "./...") diff --git a/op-service/sources/receipts_rpc.go b/op-service/sources/receipts_rpc.go index ecf2c858221..962c7391a76 100644 --- a/op-service/sources/receipts_rpc.go +++ b/op-service/sources/receipts_rpc.go @@ -329,8 +329,7 @@ func AvailableReceiptsFetchingMethods(kind RPCProviderKind) ReceiptsFetchingMeth case RPCKindQuickNode: return DebugGetRawReceipts | EthGetBlockReceipts | EthGetTransactionReceiptBatch case RPCKindInfura: - // Infura is big, but sadly does not support more optimized receipts fetching methods (yet?) - return EthGetTransactionReceiptBatch + return EthGetBlockReceipts | EthGetTransactionReceiptBatch case RPCKindParity: return ParityGetBlockReceipts | EthGetTransactionReceiptBatch case RPCKindNethermind: diff --git a/op-service/sources/receipts_test.go b/op-service/sources/receipts_test.go index 088a3d9b22c..230fcf0e72b 100644 --- a/op-service/sources/receipts_test.go +++ b/op-service/sources/receipts_test.go @@ -283,7 +283,7 @@ func TestEthClient_FetchReceipts(t *testing.T) { { name: "infura", providerKind: RPCKindInfura, - setup: fallbackCase(4, EthGetTransactionReceiptBatch), + setup: fallbackCase(4, EthGetBlockReceipts, EthGetTransactionReceiptBatch), }, { name: "nethermind", diff --git a/op-service/txmgr/queue.go b/op-service/txmgr/queue.go index ee7a03ffa92..c48687d459e 100644 --- a/op-service/txmgr/queue.go +++ b/op-service/txmgr/queue.go @@ -51,17 +51,36 @@ func (q *Queue[T]) Wait() error { return q.group.Wait() } +// handleResponse will wait for the response on the first passed channel, +// and then forward it on the second passed channel (attaching the id). It returns +// the response error or the context error if the context is canceled. +func handleResponse[T any](ctx context.Context, c chan SendResponse, d chan TxReceipt[T], id T) error { + select { + case response := <-c: + d <- TxReceipt[T]{ID: id, Receipt: response.Receipt, Err: response.Err} + return response.Err + case <-ctx.Done(): + d <- TxReceipt[T]{ID: id, Err: ctx.Err()} + return ctx.Err() + } +} + // Send will wait until the number of pending txs is below the max pending, -// and then send the next tx. +// and then send the next tx asynchronously. The nonce of the transaction is +// determined synchronously, so transactions should be confirmed on chain in +// the order they are sent using this method. // // The actual tx sending is non-blocking, with the receipt returned on the // provided receipt channel. If the channel is unbuffered, the goroutine is // blocked from completing until the channel is read from. func (q *Queue[T]) Send(id T, candidate TxCandidate, receiptCh chan TxReceipt[T]) { group, ctx := q.groupContext() - group.Go(func() error { - return q.sendTx(ctx, id, candidate, receiptCh) - }) + responseChan := make(chan SendResponse, 1) + handleResponse := func() error { + return handleResponse(ctx, responseChan, receiptCh, id) + } + group.Go(handleResponse) // This blocks until the number of handlers is below the limit + q.txMgr.SendAsync(ctx, candidate, responseChan) // Nonce management handled synchronously, i.e. before this returns } // TrySend sends the next tx, but only if the number of pending txs is below the @@ -75,19 +94,17 @@ func (q *Queue[T]) Send(id T, candidate TxCandidate, receiptCh chan TxReceipt[T] // blocked from completing until the channel is read from. func (q *Queue[T]) TrySend(id T, candidate TxCandidate, receiptCh chan TxReceipt[T]) bool { group, ctx := q.groupContext() - return group.TryGo(func() error { - return q.sendTx(ctx, id, candidate, receiptCh) - }) -} - -func (q *Queue[T]) sendTx(ctx context.Context, id T, candidate TxCandidate, receiptCh chan TxReceipt[T]) error { - receipt, err := q.txMgr.Send(ctx, candidate) - receiptCh <- TxReceipt[T]{ - ID: id, - Receipt: receipt, - Err: err, + responseChan := make(chan SendResponse, 1) + handleResponse := func() error { + return handleResponse(ctx, responseChan, receiptCh, id) + } + ok := group.TryGo(handleResponse) + if !ok { + return false + } else { + q.txMgr.SendAsync(ctx, candidate, responseChan) + return true } - return err } // groupContext returns a Group and a Context to use when sending a tx. diff --git a/op-service/txmgr/queue_test.go b/op-service/txmgr/queue_test.go index 549142c8592..00219b913ad 100644 --- a/op-service/txmgr/queue_test.go +++ b/op-service/txmgr/queue_test.go @@ -58,12 +58,11 @@ func (b *mockBackendWithNonce) NonceAt(ctx context.Context, account common.Addre func TestQueue_Send(t *testing.T) { testCases := []struct { - name string // name of the test - max uint64 // max concurrency of the queue - calls []queueCall // calls to the queue - txs []testTx // txs to generate from the factory (and potentially error in send) - nonces []uint64 // expected sent tx nonces after all calls are made - total time.Duration // approx. total time it should take to complete all queue calls + name string // name of the test + max uint64 // max concurrency of the queue + calls []queueCall // calls to the queue + txs []testTx // txs to generate from the factory (and potentially error in send) + nonces []uint64 // expected sent tx nonces after all calls are made }{ { name: "success", @@ -77,7 +76,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0, 1}, - total: 1 * time.Second, }, { name: "no limit", @@ -91,7 +89,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0, 1}, - total: 1 * time.Second, }, { name: "single threaded", @@ -105,7 +102,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0}, - total: 1 * time.Second, }, { name: "single threaded blocking", @@ -122,7 +118,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0, 1, 2}, - total: 3 * time.Second, }, { name: "dual threaded blocking", @@ -143,7 +138,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0, 1, 2, 3, 4}, - total: 3 * time.Second, }, { name: "subsequent txs fail after tx failure", @@ -159,7 +153,6 @@ func TestQueue_Send(t *testing.T) { {}, }, nonces: []uint64{0, 1}, - total: 1 * time.Second, }, } for _, test := range testCases { @@ -209,7 +202,6 @@ func TestQueue_Send(t *testing.T) { queue := NewQueue[int](ctx, mgr, test.max) // make all the queue calls given in the test case - start := time.Now() receiptChs := make([]chan TxReceipt[int], len(test.calls)) for i, c := range test.calls { msg := fmt.Sprintf("Call %d", i) @@ -223,10 +215,6 @@ func TestQueue_Send(t *testing.T) { } // wait for the queue to drain (all txs complete or failed) _ = queue.Wait() - duration := time.Since(start) - // expect the execution time within a certain window - now := time.Now() - require.WithinDuration(t, now.Add(test.total), now.Add(duration), 500*time.Millisecond, "unexpected queue transaction timing") // check that the nonces match slices.Sort(nonces) require.Equal(t, test.nonces, nonces, "expected nonces do not match") diff --git a/ops/scripts/install-foundry.sh b/ops/scripts/install-foundry.sh index 654ed6b87f7..f8ed7924bf8 100755 --- a/ops/scripts/install-foundry.sh +++ b/ops/scripts/install-foundry.sh @@ -2,6 +2,12 @@ set -e +# Check if foundryup exists, if not, install it +if ! command -v foundryup &> /dev/null; then + echo "foundryup not found, installing..." + curl -L https://foundry.paradigm.xyz | bash +fi + SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) MONOREPO_DIR=$(cd "$SCRIPTS_DIR/../../" && pwd) diff --git a/ops/scripts/install-kontrol.sh b/ops/scripts/install-kontrol.sh new file mode 100755 index 00000000000..5d4044d26b2 --- /dev/null +++ b/ops/scripts/install-kontrol.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +# Check if kup exists, if not, install it +if ! command -v kup &> /dev/null; then + echo "kup not found, installing..." + yes | bash <(curl -L https://kframework.org/install) +fi + +SCRIPTS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +MONOREPO_DIR=$(cd "$SCRIPTS_DIR/../../" && pwd) + +# Grab the correct kontrol version. +VERSION=$(jq -r .kontrol < "$MONOREPO_DIR"/versions.json) + +kup install kontrol --version v"$VERSION" diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 6bd441ca3cd..5812b129f91 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -1,15 +1,22 @@ ################################################################ -# PROFILE: DEFAULT (Local) # +# PROFILE: DEFAULT (local) # ################################################################ [profile.default] -# Compilation settings src = 'src' out = 'forge-artifacts' script = 'scripts' +build_info_path = 'artifacts/build-info' + optimizer = true optimizer_runs = 999999 + +extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] +bytecode_hash = 'none' +ast = true +evm_version = "cancun" + remappings = [ '@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts', '@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts', @@ -24,22 +31,7 @@ remappings = [ 'kontrol-cheatcodes/=lib/kontrol-cheatcodes/src', 'gelato/=lib/automate/contracts' ] -extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] -bytecode_hash = 'none' -build_info_path = 'artifacts/build-info' -ast = true -evm_version = "cancun" -# 5159 error code is selfdestruct error code -ignored_error_codes = ["transient-storage", "code-size", "init-code-size", 5159] -# We set the gas limit to max int64 to avoid running out of gas during testing, since the default -# gas limit is 1B and some of our tests require more gas than that, such as `test_callWithMinGas_noLeakageLow_succeeds`. -# We use this gas limit since it was the default gas limit prior to https://github.com/foundry-rs/foundry/pull/8274. -# Due to toml-rs limitations, if you increase the gas limit above this value it must be a string. -gas_limit = 9223372036854775807 - -# Test / Script Runner Settings -ffi = true fs_permissions = [ { access='read-write', path='./.resource-metering.csv' }, { access='read-write', path='./snapshots/' }, @@ -52,7 +44,17 @@ fs_permissions = [ { access='read', path='./kout-deployment' }, { access='read', path='./test/fixtures' }, ] -libs = ["node_modules", "lib"] + +# 5159 error code is selfdestruct error code +ignored_error_codes = ["transient-storage", "code-size", "init-code-size", 5159] +ffi = true + +# We set the gas limit to max int64 to avoid running out of gas during testing, since the default +# gas limit is 1B and some of our tests require more gas than that, such as +# test_callWithMinGas_noLeakageLow_succeeds. We use this gas limit since it was the default gas +# limit prior to https://github.com/foundry-rs/foundry/pull/8274. Due to toml-rs limitations, if +# you increase the gas limit above this value it must be a string. +gas_limit = 9223372036854775807 [fuzz] runs = 64 @@ -67,17 +69,19 @@ wrap_comments=true # PROFILE: CI # ################################################################ -[profile.ci] -fuzz = { runs = 512 } +[profile.ci.fuzz] +runs = 512 [profile.ci.invariant] runs = 256 depth = 32 -[profile.ciheavy] -# fuzz = { runs = 20000 } -# temporary reduce fuzz runs to unblock CI -fuzz = { runs = 200 } +################################################################ +# PROFILE: CIHEAVY # +################################################################ + +[profile.ciheavy.fuzz] +runs = 20000 [profile.ciheavy.invariant] runs = 128 @@ -93,7 +97,6 @@ optimizer = false ################################################################ # PROFILE: KONTROL # ################################################################ -# See test/kontrol/README.md for an explanation of how the profiles are configured [profile.kprove] src = 'test/kontrol/proofs' diff --git a/packages/contracts-bedrock/lib/lib-keccak b/packages/contracts-bedrock/lib/lib-keccak index 0115edbbc60..3b1e7bbb4cc 160000 --- a/packages/contracts-bedrock/lib/lib-keccak +++ b/packages/contracts-bedrock/lib/lib-keccak @@ -1 +1 @@ -Subproject commit 0115edbbc60b5f702392caafc3a142061e6142fa +Subproject commit 3b1e7bbb4cc23e9228097cfebe42aedaf3b8f2b9 diff --git a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol index cd1a4f86681..41133011b79 100644 --- a/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/Deploy.s.sol @@ -25,7 +25,6 @@ import { } from "scripts/deploy/DeployImplementations.s.sol"; // Contracts -import { StorageSetter } from "src/universal/StorageSetter.sol"; import { OPContractsManager } from "src/L1/OPContractsManager.sol"; // Libraries @@ -44,8 +43,6 @@ import { IL2OutputOracle } from "src/L1/interfaces/IL2OutputOracle.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { IDataAvailabilityChallenge } from "src/L1/interfaces/IDataAvailabilityChallenge.sol"; -import { IL1ERC721Bridge } from "src/L1/interfaces/IL1ERC721Bridge.sol"; -import { IL1StandardBridge } from "src/L1/interfaces/IL1StandardBridge.sol"; import { ProtocolVersion } from "src/L1/interfaces/IProtocolVersions.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; @@ -54,11 +51,7 @@ import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol" import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IMIPS } from "src/cannon/interfaces/IMIPS.sol"; -import { IMIPS2 } from "src/cannon/interfaces/IMIPS2.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; -import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; -import { IL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol"; -import { IResolvedDelegateProxy } from "src/legacy/interfaces/IResolvedDelegateProxy.sol"; /// @title Deploy /// @notice Script used to deploy a bedrock system. The entire system is deployed within the `run` function. @@ -104,18 +97,6 @@ contract Deploy is Deployer { } } - /// @notice Modifier that will only allow a function to be called on a public - /// testnet or devnet. - modifier onlyTestnetOrDevnet() { - uint256 chainid = block.chainid; - if ( - chainid == Chains.Goerli || chainid == Chains.Sepolia || chainid == Chains.LocalDevnet - || chainid == Chains.GethDevnet - ) { - _; - } - } - /// @notice Modifier that wraps a function with statediff recording. /// The returned AccountAccess[] array is then written to /// the `snapshots/state-diff/.json` output file. @@ -189,28 +170,6 @@ contract Deploy is Deployer { }); } - //////////////////////////////////////////////////////////////// - // State Changing Helper Functions // - //////////////////////////////////////////////////////////////// - - /// @notice Transfer ownership of the ProxyAdmin contract to the final system owner - function transferProxyAdminOwnership() public broadcast { - // Get the ProxyAdmin contract. - IProxyAdmin proxyAdmin = IProxyAdmin(mustGetAddress("ProxyAdmin")); - - // Transfer ownership to the final system owner if necessary. - address owner = proxyAdmin.owner(); - address finalSystemOwner = cfg.finalSystemOwner(); - if (owner != finalSystemOwner) { - proxyAdmin.transferOwnership(finalSystemOwner); - console.log("ProxyAdmin ownership transferred to final system owner at: %s", finalSystemOwner); - } - - // Make sure the ProxyAdmin owner is set to the final system owner. - owner = proxyAdmin.owner(); - require(owner == finalSystemOwner, "Deploy: ProxyAdmin ownership not transferred to final system owner"); - } - //////////////////////////////////////////////////////////////// // SetUp and Run // //////////////////////////////////////////////////////////////// @@ -320,7 +279,7 @@ contract Deploy is Deployer { bytes32 typeHash = keccak256(bytes(cfg.daCommitmentType())); bytes32 keccakHash = keccak256(bytes("KeccakCommitment")); if (typeHash == keccakHash) { - setupOpAltDA(); + deployOpAltDA(); } } @@ -509,9 +468,9 @@ contract Deploy is Deployer { _data: abi.encodeCall(IDelayedWETH.initialize, (msg.sender, ISuperchainConfig(superchainConfigProxy))) }); - setAlphabetFaultGameImplementation({ _allowUpgrade: false }); - setFastFaultGameImplementation({ _allowUpgrade: false }); - setCannonFaultGameImplementation({ _allowUpgrade: false }); + setAlphabetFaultGameImplementation(); + setFastFaultGameImplementation(); + setCannonFaultGameImplementation(); transferDisputeGameFactoryOwnership(); transferDelayedWETHOwnership(); @@ -519,109 +478,17 @@ contract Deploy is Deployer { } /// @notice Add AltDA setup to the OP chain - function setupOpAltDA() public { + function deployOpAltDA() public { console.log("Deploying OP AltDA"); deployDataAvailabilityChallengeProxy(); deployDataAvailabilityChallenge(); initializeDataAvailabilityChallenge(); } - //////////////////////////////////////////////////////////////// - // Non-Proxied Deployment Functions // - //////////////////////////////////////////////////////////////// - - /// @notice Deploy the AddressManager - function deployAddressManager() public broadcast returns (address addr_) { - // Use create instead of create2 because we need the owner to be set to msg.sender but - // forge will automatically use the create2 factory which messes up the sender. - IAddressManager manager = IAddressManager( - DeployUtils.create1AndSave({ - _save: this, - _name: "AddressManager", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IAddressManager.__constructor__, ())) - }) - ); - require(manager.owner() == msg.sender); - addr_ = address(manager); - } - - /// @notice Deploys the ProxyAdmin contract. Should NOT be used for the Superchain. - function deployProxyAdmin() public broadcast returns (address addr_) { - // Deploy the ProxyAdmin contract. - IProxyAdmin admin = IProxyAdmin( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "ProxyAdmin", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IProxyAdmin.__constructor__, (msg.sender))) - }) - ); - - // Make sure the owner was set to the deployer. - require(admin.owner() == msg.sender); - - // Set the address manager if it is not already set. - IAddressManager addressManager = IAddressManager(mustGetAddress("AddressManager")); - if (admin.addressManager() != addressManager) { - admin.setAddressManager(addressManager); - } - - // Make sure the address manager is set properly. - require(admin.addressManager() == addressManager); - - // Return the address of the deployed contract. - addr_ = address(admin); - } - - /// @notice Deploy the StorageSetter contract, used for upgrades. - function deployStorageSetter() public broadcast returns (address addr_) { - console.log("Deploying StorageSetter"); - StorageSetter setter = new StorageSetter{ salt: _implSalt() }(); - console.log("StorageSetter deployed at: %s", address(setter)); - string memory version = setter.version(); - console.log("StorageSetter version: %s", version); - addr_ = address(setter); - } - //////////////////////////////////////////////////////////////// // Proxy Deployment Functions // //////////////////////////////////////////////////////////////// - /// @notice Deploy the L1StandardBridgeProxy using a ChugSplashProxy - function deployL1StandardBridgeProxy() public broadcast returns (address addr_) { - address proxyAdmin = mustGetAddress("ProxyAdmin"); - IL1ChugSplashProxy proxy = IL1ChugSplashProxy( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "L1ChugSplashProxy", - _nick: "L1StandardBridgeProxy", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ChugSplashProxy.__constructor__, (proxyAdmin))) - }) - ); - require(EIP1967Helper.getAdmin(address(proxy)) == proxyAdmin); - addr_ = address(proxy); - } - - /// @notice Deploy the L1CrossDomainMessengerProxy using a ResolvedDelegateProxy - function deployL1CrossDomainMessengerProxy() public broadcast returns (address addr_) { - IResolvedDelegateProxy proxy = IResolvedDelegateProxy( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "ResolvedDelegateProxy", - _nick: "L1CrossDomainMessengerProxy", - _args: DeployUtils.encodeConstructor( - abi.encodeCall( - IResolvedDelegateProxy.__constructor__, - (IAddressManager(mustGetAddress("AddressManager")), "OVM_L1CrossDomainMessenger") - ) - ) - }) - ); - addr_ = address(proxy); - } - /// @notice Deploys an ERC1967Proxy contract with the ProxyAdmin as the owner. /// @param _name The name of the proxy contract to be deployed. /// @return addr_ The address of the deployed proxy contract. @@ -720,95 +587,6 @@ contract Deploy is Deployer { addr_ = address(oracle); } - /// @notice Deploy Mips VM. Deploys either MIPS or MIPS64 depending on the environment - function deployMips() public broadcast returns (address addr_) { - addr_ = DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: Config.useMultithreadedCannon() ? "MIPS64" : "MIPS", - _args: DeployUtils.encodeConstructor( - abi.encodeCall(IMIPS2.__constructor__, (IPreimageOracle(mustGetAddress("PreimageOracle")))) - ) - }); - save("Mips", address(addr_)); - } - - /// @notice Deploy the AnchorStateRegistry - function deployAnchorStateRegistry() public broadcast returns (address addr_) { - IAnchorStateRegistry anchorStateRegistry = IAnchorStateRegistry( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "AnchorStateRegistry", - _args: DeployUtils.encodeConstructor( - abi.encodeCall( - IAnchorStateRegistry.__constructor__, - (IDisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy"))) - ) - ) - }) - ); - - addr_ = address(anchorStateRegistry); - } - - /// @notice Deploy the L1StandardBridge - function deployL1StandardBridge() public broadcast returns (address addr_) { - IL1StandardBridge bridge = IL1StandardBridge( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "L1StandardBridge", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1StandardBridge.__constructor__, ())) - }) - ); - - // Override the `L1StandardBridge` contract to the deployed implementation. This is necessary - // to check the `L1StandardBridge` implementation alongside dependent contracts, which - // are always proxies. - Types.ContractSet memory contracts = _proxies(); - contracts.L1StandardBridge = address(bridge); - ChainAssertions.checkL1StandardBridge({ _contracts: contracts, _isProxy: false }); - - addr_ = address(bridge); - } - - /// @notice Deploy the L1ERC721Bridge - function deployL1ERC721Bridge() public broadcast returns (address addr_) { - IL1ERC721Bridge bridge = IL1ERC721Bridge( - DeployUtils.create2AndSave({ - _save: this, - _salt: _implSalt(), - _name: "L1ERC721Bridge", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ERC721Bridge.__constructor__, ())) - }) - ); - - // Override the `L1ERC721Bridge` contract to the deployed implementation. This is necessary - // to check the `L1ERC721Bridge` implementation alongside dependent contracts, which - // are always proxies. - Types.ContractSet memory contracts = _proxies(); - contracts.L1ERC721Bridge = address(bridge); - - ChainAssertions.checkL1ERC721Bridge({ _contracts: contracts, _isProxy: false }); - - addr_ = address(bridge); - } - - /// @notice Transfer ownership of the address manager to the ProxyAdmin - function transferAddressManagerOwnership() public broadcast { - console.log("Transferring AddressManager ownership to IProxyAdmin"); - IAddressManager addressManager = IAddressManager(mustGetAddress("AddressManager")); - address owner = addressManager.owner(); - address proxyAdmin = mustGetAddress("ProxyAdmin"); - if (owner != proxyAdmin) { - addressManager.transferOwnership(proxyAdmin); - console.log("AddressManager ownership transferred to %s", proxyAdmin); - } - - require(addressManager.owner() == proxyAdmin); - } - /// @notice Deploy the DataAvailabilityChallenge function deployDataAvailabilityChallenge() public broadcast returns (address addr_) { IDataAvailabilityChallenge dac = IDataAvailabilityChallenge( @@ -941,6 +719,61 @@ contract Deploy is Deployer { ChainAssertions.checkOptimismPortal({ _contracts: _proxies(), _cfg: cfg, _isProxy: true }); } + /// @notice Initialize the DataAvailabilityChallenge + function initializeDataAvailabilityChallenge() public broadcast { + console.log("Upgrading and initializing DataAvailabilityChallenge proxy"); + address dataAvailabilityChallengeProxy = mustGetAddress("DataAvailabilityChallengeProxy"); + address dataAvailabilityChallenge = mustGetAddress("DataAvailabilityChallenge"); + + address finalSystemOwner = cfg.finalSystemOwner(); + uint256 daChallengeWindow = cfg.daChallengeWindow(); + uint256 daResolveWindow = cfg.daResolveWindow(); + uint256 daBondSize = cfg.daBondSize(); + uint256 daResolverRefundPercentage = cfg.daResolverRefundPercentage(); + + IProxyAdmin proxyAdmin = IProxyAdmin(payable(mustGetAddress("ProxyAdmin"))); + proxyAdmin.upgradeAndCall({ + _proxy: payable(dataAvailabilityChallengeProxy), + _implementation: dataAvailabilityChallenge, + _data: abi.encodeCall( + IDataAvailabilityChallenge.initialize, + (finalSystemOwner, daChallengeWindow, daResolveWindow, daBondSize, daResolverRefundPercentage) + ) + }); + + IDataAvailabilityChallenge dac = IDataAvailabilityChallenge(payable(dataAvailabilityChallengeProxy)); + string memory version = dac.version(); + console.log("DataAvailabilityChallenge version: %s", version); + + require(dac.owner() == finalSystemOwner); + require(dac.challengeWindow() == daChallengeWindow); + require(dac.resolveWindow() == daResolveWindow); + require(dac.bondSize() == daBondSize); + require(dac.resolverRefundPercentage() == daResolverRefundPercentage); + } + + //////////////////////////////////////////////////////////////// + // Ownership Transfer Helper Functions // + //////////////////////////////////////////////////////////////// + + /// @notice Transfer ownership of the ProxyAdmin contract to the final system owner + function transferProxyAdminOwnership() public broadcast { + // Get the ProxyAdmin contract. + IProxyAdmin proxyAdmin = IProxyAdmin(mustGetAddress("ProxyAdmin")); + + // Transfer ownership to the final system owner if necessary. + address owner = proxyAdmin.owner(); + address finalSystemOwner = cfg.finalSystemOwner(); + if (owner != finalSystemOwner) { + proxyAdmin.transferOwnership(finalSystemOwner); + console.log("ProxyAdmin ownership transferred to final system owner at: %s", finalSystemOwner); + } + + // Make sure the ProxyAdmin owner is set to the final system owner. + owner = proxyAdmin.owner(); + require(owner == finalSystemOwner, "Deploy: ProxyAdmin ownership not transferred to final system owner"); + } + /// @notice Transfer ownership of the DisputeGameFactory contract to the final system owner function transferDisputeGameFactoryOwnership() public broadcast { console.log("Transferring DisputeGameFactory ownership to Safe"); @@ -997,6 +830,10 @@ contract Deploy is Deployer { }); } + /////////////////////////////////////////////////////////// + // Proofs setup helper functions // + /////////////////////////////////////////////////////////// + /// @notice Load the appropriate mips absolute prestate for devenets depending on config environment. function loadMipsAbsolutePrestate() internal returns (Claim mipsAbsolutePrestate_) { if (block.chainid == Chains.LocalDevnet || block.chainid == Chains.GethDevnet) { @@ -1050,7 +887,7 @@ contract Deploy is Deployer { } /// @notice Sets the implementation for the `CANNON` game type in the `DisputeGameFactory` - function setCannonFaultGameImplementation(bool _allowUpgrade) public broadcast { + function setCannonFaultGameImplementation() public broadcast { console.log("Setting Cannon FaultDisputeGame implementation"); IDisputeGameFactory factory = IDisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); IDelayedWETH weth = IDelayedWETH(mustGetAddress("DelayedWETHProxy")); @@ -1058,7 +895,6 @@ contract Deploy is Deployer { // Set the Cannon FaultDisputeGame implementation in the factory. _setFaultGameImplementation({ _factory: factory, - _allowUpgrade: _allowUpgrade, _params: FaultDisputeGameParams({ anchorStateRegistry: IAnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")), weth: weth, @@ -1071,30 +907,8 @@ contract Deploy is Deployer { }); } - /// @notice Sets the implementation for the `PERMISSIONED_CANNON` game type in the `DisputeGameFactory` - function setPermissionedCannonFaultGameImplementation(bool _allowUpgrade) public broadcast { - console.log("Setting Cannon PermissionedDisputeGame implementation"); - IDisputeGameFactory factory = IDisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); - IDelayedWETH weth = IDelayedWETH(mustGetAddress("PermissionedDelayedWETHProxy")); - - // Deploys and sets the Permissioned FaultDisputeGame implementation in the factory. - _setFaultGameImplementation({ - _factory: factory, - _allowUpgrade: _allowUpgrade, - _params: FaultDisputeGameParams({ - anchorStateRegistry: IAnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")), - weth: weth, - gameType: GameTypes.PERMISSIONED_CANNON, - absolutePrestate: loadMipsAbsolutePrestate(), - faultVm: IBigStepper(mustGetAddress("Mips")), - maxGameDepth: cfg.faultGameMaxDepth(), - maxClockDuration: Duration.wrap(uint64(cfg.faultGameMaxClockDuration())) - }) - }); - } - /// @notice Sets the implementation for the `ALPHABET` game type in the `DisputeGameFactory` - function setAlphabetFaultGameImplementation(bool _allowUpgrade) public onlyDevnet broadcast { + function setAlphabetFaultGameImplementation() public onlyDevnet broadcast { console.log("Setting Alphabet FaultDisputeGame implementation"); IDisputeGameFactory factory = IDisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); IDelayedWETH weth = IDelayedWETH(mustGetAddress("DelayedWETHProxy")); @@ -1102,7 +916,6 @@ contract Deploy is Deployer { Claim outputAbsolutePrestate = Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())); _setFaultGameImplementation({ _factory: factory, - _allowUpgrade: _allowUpgrade, _params: FaultDisputeGameParams({ anchorStateRegistry: IAnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")), weth: weth, @@ -1117,7 +930,7 @@ contract Deploy is Deployer { } /// @notice Sets the implementation for the `ALPHABET` game type in the `DisputeGameFactory` - function setFastFaultGameImplementation(bool _allowUpgrade) public onlyDevnet broadcast { + function setFastFaultGameImplementation() public onlyDevnet broadcast { console.log("Setting Fast FaultDisputeGame implementation"); IDisputeGameFactory factory = IDisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); IDelayedWETH weth = IDelayedWETH(mustGetAddress("DelayedWETHProxy")); @@ -1136,7 +949,6 @@ contract Deploy is Deployer { ); _setFaultGameImplementation({ _factory: factory, - _allowUpgrade: _allowUpgrade, _params: FaultDisputeGameParams({ anchorStateRegistry: IAnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")), weth: weth, @@ -1153,12 +965,11 @@ contract Deploy is Deployer { /// @notice Sets the implementation for the given fault game type in the `DisputeGameFactory`. function _setFaultGameImplementation( IDisputeGameFactory _factory, - bool _allowUpgrade, FaultDisputeGameParams memory _params ) internal { - if (address(_factory.gameImpls(_params.gameType)) != address(0) && !_allowUpgrade) { + if (address(_factory.gameImpls(_params.gameType)) != address(0)) { console.log( "[WARN] DisputeGameFactoryProxy: `FaultDisputeGame` implementation already set for game type: %s", vm.toString(GameType.unwrap(_params.gameType)) @@ -1218,39 +1029,6 @@ contract Deploy is Deployer { ); } - /// @notice Initialize the DataAvailabilityChallenge - function initializeDataAvailabilityChallenge() public broadcast { - console.log("Upgrading and initializing DataAvailabilityChallenge proxy"); - address dataAvailabilityChallengeProxy = mustGetAddress("DataAvailabilityChallengeProxy"); - address dataAvailabilityChallenge = mustGetAddress("DataAvailabilityChallenge"); - - address finalSystemOwner = cfg.finalSystemOwner(); - uint256 daChallengeWindow = cfg.daChallengeWindow(); - uint256 daResolveWindow = cfg.daResolveWindow(); - uint256 daBondSize = cfg.daBondSize(); - uint256 daResolverRefundPercentage = cfg.daResolverRefundPercentage(); - - IProxyAdmin proxyAdmin = IProxyAdmin(payable(mustGetAddress("ProxyAdmin"))); - proxyAdmin.upgradeAndCall({ - _proxy: payable(dataAvailabilityChallengeProxy), - _implementation: dataAvailabilityChallenge, - _data: abi.encodeCall( - IDataAvailabilityChallenge.initialize, - (finalSystemOwner, daChallengeWindow, daResolveWindow, daBondSize, daResolverRefundPercentage) - ) - }); - - IDataAvailabilityChallenge dac = IDataAvailabilityChallenge(payable(dataAvailabilityChallengeProxy)); - string memory version = dac.version(); - console.log("DataAvailabilityChallenge version: %s", version); - - require(dac.owner() == finalSystemOwner); - require(dac.challengeWindow() == daChallengeWindow); - require(dac.resolveWindow() == daResolveWindow); - require(dac.bondSize() == daBondSize); - require(dac.resolverRefundPercentage() == daResolverRefundPercentage); - } - /// @notice Get the DeployInput struct to use for testing function getDeployInput() public view returns (OPContractsManager.DeployInput memory) { OutputRoot memory testOutputRoot = OutputRoot({ @@ -1296,6 +1074,7 @@ contract Deploy is Deployer { }); } + /// @notice Reset the initialized value on a proxy contract so that it can be initialized again function resetInitializedProxy(string memory _contractName) internal { console.log("resetting initialized value on %s Proxy", _contractName); address proxy = mustGetAddress(string.concat(_contractName, "Proxy")); diff --git a/packages/contracts-bedrock/scripts/deploy/DeployAsterisc.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployAsterisc.s.sol new file mode 100644 index 00000000000..09601eb0d8e --- /dev/null +++ b/packages/contracts-bedrock/scripts/deploy/DeployAsterisc.s.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.15; + +// Forge +import { Script } from "forge-std/Script.sol"; + +// Scripts +import { BaseDeployIO } from "scripts/deploy/BaseDeployIO.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +// Interfaces +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; +import { IRISCV } from "src/vendor/asterisc/interfaces/IRISCV.sol"; + +/// @title DeployAsteriscInput +contract DeployAsteriscInput is BaseDeployIO { + // Specify the PreimageOracle to use + address internal _preimageOracle; + + function set(bytes4 _sel, address _value) public { + if (_sel == this.preimageOracle.selector) { + require(_value != address(0), "DeployAsterisc: preimageOracle cannot be empty"); + _preimageOracle = _value; + } else { + revert("DeployAsterisc: unknown selector"); + } + } + + function preimageOracle() public view returns (address) { + require(_preimageOracle != address(0), "DeployAsterisc: preimageOracle not set"); + return _preimageOracle; + } +} + +/// @title DeployAsteriscOutput +contract DeployAsteriscOutput is BaseDeployIO { + IRISCV internal _asteriscSingleton; + + function set(bytes4 _sel, address _value) public { + if (_sel == this.asteriscSingleton.selector) { + require(_value != address(0), "DeployAsterisc: asteriscSingleton cannot be zero address"); + _asteriscSingleton = IRISCV(_value); + } else { + revert("DeployAsterisc: unknown selector"); + } + } + + function checkOutput(DeployAsteriscInput _mi) public view { + DeployUtils.assertValidContractAddress(address(_asteriscSingleton)); + assertValidDeploy(_mi); + } + + function asteriscSingleton() public view returns (IRISCV) { + DeployUtils.assertValidContractAddress(address(_asteriscSingleton)); + return _asteriscSingleton; + } + + function assertValidDeploy(DeployAsteriscInput _mi) public view { + assertValidAsteriscSingleton(_mi); + } + + function assertValidAsteriscSingleton(DeployAsteriscInput _mi) internal view { + IRISCV asterisc = asteriscSingleton(); + + require(address(asterisc.oracle()) == address(_mi.preimageOracle()), "ASTERISC-10"); + } +} + +/// @title DeployAsterisc +contract DeployAsterisc is Script { + function run(DeployAsteriscInput _mi, DeployAsteriscOutput _mo) public { + DeployAsteriscSingleton(_mi, _mo); + _mo.checkOutput(_mi); + } + + function DeployAsteriscSingleton(DeployAsteriscInput _mi, DeployAsteriscOutput _mo) internal { + IPreimageOracle preimageOracle = IPreimageOracle(_mi.preimageOracle()); + vm.broadcast(msg.sender); + IRISCV singleton = IRISCV( + DeployUtils.create1({ + _name: "RISCV", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IRISCV.__constructor__, (preimageOracle))) + }) + ); + + vm.label(address(singleton), "AsteriscSingleton"); + _mo.set(_mo.asteriscSingleton.selector, address(singleton)); + } +} diff --git a/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol index c128260a21d..153e4b65fdb 100644 --- a/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol +++ b/packages/contracts-bedrock/scripts/deploy/DeployDisputeGame.s.sol @@ -18,8 +18,6 @@ import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDi import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; -import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; -import { IMIPS } from "src/cannon/interfaces/IMIPS.sol"; /// @title DeployDisputeGameInput contract DeployDisputeGameInput is BaseDeployIO { @@ -27,13 +25,6 @@ contract DeployDisputeGameInput is BaseDeployIO { string internal _release; string internal _standardVersionsToml; - // Specify which MIPS version to use. - uint256 internal _mipsVersion; - - // All inputs required to deploy PreimageOracle. - uint256 internal _minProposalSizeBytes; - uint256 internal _challengePeriodSeconds; - // Specify which game kind is being deployed here. string internal _gameKind; @@ -46,6 +37,7 @@ contract DeployDisputeGameInput is BaseDeployIO { uint256 internal _maxClockDuration; IDelayedWETH internal _delayedWethProxy; IAnchorStateRegistry internal _anchorStateRegistryProxy; + IBigStepper internal _vm; uint256 internal _l2ChainId; // Additional inputs required to deploy PermissionedDisputeGame. @@ -53,16 +45,7 @@ contract DeployDisputeGameInput is BaseDeployIO { address internal _challenger; function set(bytes4 _sel, uint256 _value) public { - if (_sel == this.mipsVersion.selector) { - require(_value == 1 || _value == 2, "DeployDisputeGame: unknown mips version"); - _mipsVersion = _value; - } else if (_sel == this.minProposalSizeBytes.selector) { - require(_value != 0, "DeployDisputeGame: minProposalSizeBytes cannot be zero"); - _minProposalSizeBytes = _value; - } else if (_sel == this.challengePeriodSeconds.selector) { - require(_value != 0, "DeployDisputeGame: challengePeriodSeconds cannot be zero"); - _challengePeriodSeconds = _value; - } else if (_sel == this.gameType.selector) { + if (_sel == this.gameType.selector) { require(_value <= type(uint32).max, "DeployDisputeGame: gameType must fit inside uint32"); _gameType = _value; } else if (_sel == this.maxGameDepth.selector) { @@ -88,7 +71,9 @@ contract DeployDisputeGameInput is BaseDeployIO { } function set(bytes4 _sel, address _value) public { - if (_sel == this.delayedWethProxy.selector) { + if (_sel == this.vmAddress.selector) { + _vm = IBigStepper(_value); + } else if (_sel == this.delayedWethProxy.selector) { require(_value != address(0), "DeployDisputeGame: delayedWethProxy cannot be zero address"); _delayedWethProxy = IDelayedWETH(payable(_value)); } else if (_sel == this.anchorStateRegistryProxy.selector) { @@ -133,20 +118,8 @@ contract DeployDisputeGameInput is BaseDeployIO { return _standardVersionsToml; } - function mipsVersion() public view returns (uint256) { - require(_mipsVersion != 0, "DeployDisputeGame: mipsVersion not set"); - require(_mipsVersion == 1 || _mipsVersion == 2, "DeployDisputeGame: unknown mips version"); - return _mipsVersion; - } - - function minProposalSizeBytes() public view returns (uint256) { - require(_minProposalSizeBytes != 0, "DeployDisputeGame: minProposalSizeBytes not set"); - return _minProposalSizeBytes; - } - - function challengePeriodSeconds() public view returns (uint256) { - require(_challengePeriodSeconds != 0, "DeployDisputeGame: challengePeriodSeconds not set"); - return _challengePeriodSeconds; + function vmAddress() public view returns (IBigStepper) { + return _vm; } function gameKind() public view returns (string memory) { @@ -228,65 +201,30 @@ contract DeployDisputeGameOutput is BaseDeployIO { // PermissionedDisputeGame is used as the type here because it has all of the same functions as // FaultDisputeGame but with the added proposer and challenger fields. IPermissionedDisputeGame internal _disputeGameImpl; - IMIPS internal _mipsSingleton; - IPreimageOracle internal _preimageOracleSingleton; function set(bytes4 _sel, address _value) public { if (_sel == this.disputeGameImpl.selector) { require(_value != address(0), "DeployDisputeGame: disputeGameImpl cannot be zero address"); _disputeGameImpl = IPermissionedDisputeGame(_value); - } else if (_sel == this.mipsSingleton.selector) { - require(_value != address(0), "DeployDisputeGame: mipsSingleton cannot be zero address"); - _mipsSingleton = IMIPS(_value); - } else if (_sel == this.preimageOracleSingleton.selector) { - require(_value != address(0), "DeployDisputeGame: preimageOracleSingleton cannot be zero address"); - _preimageOracleSingleton = IPreimageOracle(_value); } else { revert("DeployDisputeGame: unknown selector"); } } function checkOutput(DeployDisputeGameInput _dgi) public view { - DeployUtils.assertValidContractAddress(address(_preimageOracleSingleton)); - DeployUtils.assertValidContractAddress(address(_mipsSingleton)); DeployUtils.assertValidContractAddress(address(_disputeGameImpl)); assertValidDeploy(_dgi); } - function preimageOracleSingleton() public view returns (IPreimageOracle) { - DeployUtils.assertValidContractAddress(address(_preimageOracleSingleton)); - return _preimageOracleSingleton; - } - - function mipsSingleton() public view returns (IMIPS) { - DeployUtils.assertValidContractAddress(address(_mipsSingleton)); - return _mipsSingleton; - } - function disputeGameImpl() public view returns (IPermissionedDisputeGame) { DeployUtils.assertValidContractAddress(address(_disputeGameImpl)); return _disputeGameImpl; } function assertValidDeploy(DeployDisputeGameInput _dgi) public view { - assertValidPreimageOracleSingleton(_dgi); - assertValidMipsSingleton(_dgi); assertValidDisputeGameImpl(_dgi); } - function assertValidPreimageOracleSingleton(DeployDisputeGameInput _dgi) internal view { - IPreimageOracle oracle = preimageOracleSingleton(); - - require(oracle.minProposalSize() == _dgi.minProposalSizeBytes(), "PO-10"); - require(oracle.challengePeriod() == _dgi.challengePeriodSeconds(), "PO-20"); - } - - function assertValidMipsSingleton(DeployDisputeGameInput) internal view { - IMIPS mips = mipsSingleton(); - - require(address(mips.oracle()) == address(preimageOracleSingleton()), "MIPS-10"); - } - function assertValidDisputeGameImpl(DeployDisputeGameInput _dgi) internal view { IPermissionedDisputeGame game = disputeGameImpl(); @@ -295,7 +233,7 @@ contract DeployDisputeGameOutput is BaseDeployIO { require(game.splitDepth() == _dgi.splitDepth(), "DG-30"); require(game.clockExtension().raw() == uint64(_dgi.clockExtension()), "DG-40"); require(game.maxClockDuration().raw() == uint64(_dgi.maxClockDuration()), "DG-50"); - require(game.vm() == IBigStepper(address(mipsSingleton())), "DG-60"); + require(game.vm() == _dgi.vmAddress(), "DG-60"); require(game.weth() == _dgi.delayedWethProxy(), "DG-70"); require(game.anchorStateRegistry() == _dgi.anchorStateRegistryProxy(), "DG-80"); require(game.l2ChainId() == _dgi.l2ChainId(), "DG-90"); @@ -326,68 +264,10 @@ contract DeployDisputeGame is Script { } function run(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) public { - deployPreimageOracleSingleton(_dgi, _dgo); - deployMipsSingleton(_dgi, _dgo); deployDisputeGameImpl(_dgi, _dgo); _dgo.checkOutput(_dgi); } - function deployPreimageOracleSingleton(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { - string memory release = _dgi.release(); - string memory stdVerToml = _dgi.standardVersionsToml(); - string memory contractName = "preimage_oracle"; - IPreimageOracle singleton; - - address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); - if (existingImplementation != address(0)) { - singleton = IPreimageOracle(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { - uint256 minProposalSizeBytes = _dgi.minProposalSizeBytes(); - uint256 challengePeriodSeconds = _dgi.challengePeriodSeconds(); - vm.broadcast(msg.sender); - singleton = IPreimageOracle( - DeployUtils.create1({ - _name: "PreimageOracle", - _args: DeployUtils.encodeConstructor( - abi.encodeCall(IPreimageOracle.__constructor__, (minProposalSizeBytes, challengePeriodSeconds)) - ) - }) - ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); - } - - vm.label(address(singleton), "PreimageOracleSingleton"); - _dgo.set(_dgo.preimageOracleSingleton.selector, address(singleton)); - } - - function deployMipsSingleton(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { - string memory release = _dgi.release(); - string memory stdVerToml = _dgi.standardVersionsToml(); - string memory contractName = "mips"; - IMIPS singleton; - - address existingImplementation = getReleaseAddress(release, contractName, stdVerToml); - if (existingImplementation != address(0)) { - singleton = IMIPS(payable(existingImplementation)); - } else if (isDevelopRelease(release)) { - uint256 mipsVersion = _dgi.mipsVersion(); - IPreimageOracle preimageOracle = IPreimageOracle(address(_dgo.preimageOracleSingleton())); - vm.broadcast(msg.sender); - singleton = IMIPS( - DeployUtils.create1({ - _name: mipsVersion == 1 ? "MIPS" : "MIPS64", - _args: DeployUtils.encodeConstructor(abi.encodeCall(IMIPS.__constructor__, (preimageOracle))) - }) - ); - } else { - revert(string.concat("DeployImplementations: failed to deploy release ", release)); - } - - vm.label(address(singleton), "MIPSSingleton"); - _dgo.set(_dgo.mipsSingleton.selector, address(singleton)); - } - function deployDisputeGameImpl(DeployDisputeGameInput _dgi, DeployDisputeGameOutput _dgo) internal { // Shove the arguments into a struct to avoid stack-too-deep errors. DisputeGameConstructorArgs memory args = DisputeGameConstructorArgs({ @@ -397,7 +277,7 @@ contract DeployDisputeGame is Script { splitDepth: _dgi.splitDepth(), clockExtension: Duration.wrap(uint64(_dgi.clockExtension())), maxClockDuration: Duration.wrap(uint64(_dgi.maxClockDuration())), - gameVm: IBigStepper(address(_dgo.mipsSingleton())), + gameVm: IBigStepper(address(_dgi.vmAddress())), delayedWethProxy: _dgi.delayedWethProxy(), anchorStateRegistryProxy: _dgi.anchorStateRegistryProxy(), l2ChainId: _dgi.l2ChainId(), diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example index 02d67a4bad0..2d7d84e67aa 100644 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/.env.example @@ -2,6 +2,8 @@ # ↓ Required ↓ # ############################################## +# NOTE: The deploy config must be provided as a first argument to `just run`! + # The network to deploy the contracts to. # Must be one of 'mainnet', 'sepolia' NETWORK= @@ -15,12 +17,6 @@ ETH_RPC_URL= # Private key used to deploy the new contracts for this upgrade PRIVATE_KEY= -# Path to the deploy config JSON file -DEPLOY_CONFIG_PATH= - -# Path to the folder where output artifacts will be stored -OUTPUT_FOLDER_PATH= - # Address of deployed `PreimageOracle` contract. PREIMAGE_ORACLE_ADDR= diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/README.md b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md index f16127ffebb..a671362bff8 100644 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/README.md +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/README.md @@ -37,5 +37,20 @@ cp .env.example .env && vim .env # - Deploy the new smart contract implementations. # - Optionally, generate a safe upgrade bundle. # - Optionally, generate a `superchain-ops` upgrade task. -just run +# +# The first argument must be the absolute path to your deploy-config.json. +# You can optionally specify an output folder path different from the default `output/` as a +# second argument to `just run`, also as an absolute path. +just run $(realpath path/to/deploy-config.json) ``` + +Note that in order to build the Docker image, you have to allow Docker to use at least 16GB of +memory, or the Solidity compilations may fail. Docker's default is only 8GB. + +The `deploy-config.json` that you use for your chain must set the latest `faultGameAbsolutePrestate` +value, not the one at deployment. There's currently one available that includes the Sepolia +Superchain Holocene activations for Base, OP, Mode and Zora: +`0x03925193e3e89f87835bbdf3a813f60b2aa818a36bbe71cd5d8fd7e79f5e8afe` + +If you want to make local modifications to the scripts in `scripts/`, you need to build the Docker +image again with `just build-image` before running `just run`. diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/justfile b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile index 08cdc771ac9..ac08df68132 100644 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/justfile +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/justfile @@ -4,16 +4,19 @@ default: # Run the deployment / upgrade generation image. If the image is not present locally, # it will be built. -run *args='': +run deploy-config-path output-folder-path="$(pwd)/output/" *args='': #!/bin/bash if [ ! "$(docker images -q op-holocene-upgrade:local 2> /dev/null)" ]; then just build-image fi + mkdir -p {{output-folder-path}} + # Run the deployment. - docker run \ + docker run -it \ --rm \ - -v $OUTPUT_FOLDER_PATH/:/output \ + -v {{output-folder-path}}:/output \ + -v {{deploy-config-path}}:/app/packages/contracts-bedrock/deploy-config/deploy-config.json \ --env-file=.env \ op-holocene-upgrade:local {{args}} @@ -22,5 +25,5 @@ build-image: docker build \ -t op-holocene-upgrade:local \ -f upgrade.dockerfile \ - --build-arg REV=op-contracts/v1.8.0-rc.1 \ + --build-arg REV=op-contracts/v1.8.0-rc.2 \ . diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh index 95fe1a43c23..7ae6a55ed72 100755 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/main.sh @@ -37,8 +37,7 @@ export NETWORK="${NETWORK:?NETWORK must be set}" export ETHERSCAN_API_KEY=${ETHERSCAN_API_KEY:?ETHERSCAN_API_KEY must be set} export ETH_RPC_URL=${ETH_RPC_URL:?ETH_RPC_URL must be set} export PRIVATE_KEY=${PRIVATE_KEY:?PRIVATE_KEY must be set} -export BASE_DEPLOY_CONFIG_PATH=${DEPLOY_CONFIG_PATH:?DEPLOY_CONFIG_PATH must be set} -export OUTPUT_FOLDER_PATH=${OUTPUT_FOLDER_PATH:?OUTPUT_FOLDER_PATH must be set} +export OUTPUT_FOLDER_PATH="/output" export SYSTEM_CONFIG_IMPL_ADDR=${SYSTEM_CONFIG_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "system_config")} export MIPS_IMPL_ADDR=${MIPS_IMPL_ADDR:-$(fetch_standard_address "$NETWORK" "$RELEASE" "mips")} export PREIMAGE_ORACLE_ADDR=${PREIMAGE_ORACLE_ADDR:?PREIMAGE_ORACLE_ADDR must be set} @@ -53,8 +52,8 @@ export USE_PERMISSIONLESS_FAULT_PROOFS=${USE_PERMISSIONLESS_FAULT_PROOFS:?USE_PE # Sanity check FP configuration. if [[ $USE_PERMISSIONLESS_FAULT_PROOFS == true && $USE_FAULT_PROOFS == false ]]; then - echo "Error: USE_PERMISSIONLESS_FAULT_PROOFS cannot be true if USE_FAULT_PROOFS is false" - exit 1 + echo "Error: USE_PERMISSIONLESS_FAULT_PROOFS cannot be true if USE_FAULT_PROOFS is false" + exit 1 fi # Make the output folder, if it doesn't exist @@ -63,26 +62,28 @@ mkdir -p "$OUTPUT_FOLDER_PATH" # Find the contracts-bedrock directory CONTRACTS_BEDROCK_DIR=$(pwd) while [[ "$CONTRACTS_BEDROCK_DIR" != "/" && "${CONTRACTS_BEDROCK_DIR##*/}" != "contracts-bedrock" ]]; do - CONTRACTS_BEDROCK_DIR=$(dirname "$CONTRACTS_BEDROCK_DIR") + CONTRACTS_BEDROCK_DIR=$(dirname "$CONTRACTS_BEDROCK_DIR") done # Error out if we couldn't find it for some reason if [[ "$CONTRACTS_BEDROCK_DIR" == "/" ]]; then - echo "Error: 'contracts-bedrock' directory not found" - exit 1 + echo "Error: 'contracts-bedrock' directory not found" + exit 1 fi -# Set file paths from command-line arguments +# The deploy config is mounted via Docker to this file export DEPLOY_CONFIG_PATH="$CONTRACTS_BEDROCK_DIR/deploy-config/deploy-config.json" -# Copy the files into the paths so that the script can actually access it -cp "$BASE_DEPLOY_CONFIG_PATH" "$DEPLOY_CONFIG_PATH" - -# Run deploy.sh +# Run deploy.sh if deployments.json does not exist DEPLOY_LOG_PATH="$OUTPUT_FOLDER_PATH/deploy.log" -if ! "$SCRIPT_DIR/deploy.sh" | tee "$DEPLOY_LOG_PATH"; then +DEPLOYMENTS_JSON_PATH="$OUTPUT_FOLDER_PATH/deployments.json" +if [[ ! -f "$DEPLOYMENTS_JSON_PATH" ]]; then + if ! "$SCRIPT_DIR/deploy.sh" | tee "$DEPLOY_LOG_PATH"; then echo "Error: deploy.sh failed" exit 1 + fi +else + prompt "Skipping deployment as $DEPLOYMENTS_JSON_PATH already exists. Continue?" fi # Extract the addresses from the deployment logs @@ -102,8 +103,7 @@ reqenv "FDG_IMPL" reqenv "PDG_IMPL" # Generate deployments.json with extracted addresses -DEPLOYMENTS_JSON_PATH="$OUTPUT_FOLDER_PATH/deployments.json" -cat << EOF > "$DEPLOYMENTS_JSON_PATH" +cat <"$DEPLOYMENTS_JSON_PATH" { "SystemConfig": "$SYSTEM_CONFIG_IMPL", "MIPS": "$MIPS_IMPL", @@ -121,16 +121,16 @@ prompt "Generate safe upgrade bundle for SystemConfig?" # Generate the system config upgrade bundle if ! "$SCRIPT_DIR/sys-cfg-bundle.sh"; then - echo "Error: sys-cfg-bundle.sh failed" - exit 1 + echo "Error: sys-cfg-bundle.sh failed" + exit 1 fi prompt "Generate superchain-ops upgrade task for SystemConfig upgrade bundle?" # Generate the superchain-ops upgrade task if ! "$SCRIPT_DIR/sc-ops-sys-cfg.sh"; then - echo "Error: sc-ops-sys-cfg.sh failed" - exit 1 + echo "Error: sc-ops-sys-cfg.sh failed" + exit 1 fi if [[ $USE_FAULT_PROOFS == true ]]; then @@ -138,15 +138,15 @@ if [[ $USE_FAULT_PROOFS == true ]]; then # Generate the proofs contracts' upgrade bundle if ! "$SCRIPT_DIR/proofs-bundle.sh"; then - echo "Error: proofs-bundle.sh failed" - exit 1 + echo "Error: proofs-bundle.sh failed" + exit 1 fi prompt "Generate superchain-ops upgrade task for proofs contracts upgrade bundle?" # Generate the superchain-ops upgrade task if ! "$SCRIPT_DIR/sc-ops-proofs.sh"; then - echo "Error: sc-ops-proofs.sh failed" - exit 1 + echo "Error: sc-ops-proofs.sh failed" + exit 1 fi fi diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh index 85295d3040f..bc0b0c7ff31 100755 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/proofs-bundle.sh @@ -31,24 +31,24 @@ TX_1_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 1 "$PDG_IMPL") TX_2_PAYLOAD=$(cast calldata "setImplementation(uint32,address)" 0 "$FDG_IMPL") # Replace variables -sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" -sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$BUNDLE_PATH" -sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" -sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" +sed -i "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i "s/\$PDG_IMPL/$PDG_IMPL/g" "$BUNDLE_PATH" +sed -i "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" +sed -i "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" # Conditionally, if the FDG is being deployed, append the bundle extension if [ "$USE_PERMISSIONLESS_FAULT_PROOFS" == true ]; then echo "✨ USE_PERMISSIONLESS_FAULT_PROOFS=true | Adding FDG deployment to upgrade bundle." jq --argjson fdg_extension "$(cat ./templates/fdg_bundle_extension.json)" \ '.transactions += [$fdg_extension]' \ - "$BUNDLE_PATH" > "$BUNDLE_PATH.tmp" + "$BUNDLE_PATH" >"$BUNDLE_PATH.tmp" mv "$BUNDLE_PATH.tmp" "$BUNDLE_PATH" # Replace variables - sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$BUNDLE_PATH" - sed -i '' "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" + sed -i "s/\$FDG_IMPL/$FDG_IMPL/g" "$BUNDLE_PATH" + sed -i "s/\$TX_2_PAYLOAD/$TX_2_PAYLOAD/g" "$BUNDLE_PATH" fi -sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$BUNDLE_PATH" +sed -i "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$BUNDLE_PATH" echo "✨ Generated proof contracts upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh index d4ff18db573..ec386727d3b 100755 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-proofs.sh @@ -27,9 +27,9 @@ cp -R "$SCRIPT_DIR/../templates/proofs-sc-ops-task/." "$TASK_DIR/" msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" # Generate the README -sed -i '' "s/\$MIPS_IMPL/$MIPS_IMPL/g" "$TASK_DIR/README.md" -sed -i '' "s/\$FDG_IMPL/$FDG_IMPL/g" "$TASK_DIR/README.md" -sed -i '' "s/\$PDG_IMPL/$PDG_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$MIPS_IMPL/$MIPS_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$FDG_IMPL/$FDG_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$PDG_IMPL/$PDG_IMPL/g" "$TASK_DIR/README.md" # Generate the validation doc OLD_FDG=$(cast call "$DISPUTE_GAME_FACTORY_PROXY_ADDR" "gameImpls(uint32)" 0) @@ -40,8 +40,8 @@ PADDED_OLD_PDG=$(cast 2u "$OLD_PDG") PADDED_FDG_IMPL=$(cast 2u "$FDG_IMPL") PADDED_PDG_IMPL=$(cast 2u "$PDG_IMPL") -sed -i '' "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$OLD_FDG/$PADDED_OLD_FDG/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$FDG_IMPL/$PADDED_FDG_IMPL/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$PDG_IMPL/$PADDED_PDG_IMPL/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$OLD_PDG/$PADDED_OLD_PDG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$DISPUTE_GAME_FACTORY_PROXY_ADDR/$DISPUTE_GAME_FACTORY_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_FDG/$PADDED_OLD_FDG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$FDG_IMPL/$PADDED_FDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$PDG_IMPL/$PADDED_PDG_IMPL/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_PDG/$PADDED_OLD_PDG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh index b0e4a57b32b..a87de445d3d 100755 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sc-ops-sys-cfg.sh @@ -25,7 +25,7 @@ cp -R "$SCRIPT_DIR/../templates/sys-cfg-sc-ops-task/." "$TASK_DIR/" msup render -i "$TASK_DIR/input.json" -o "$TASK_DIR/OVERVIEW.md" # Generate the README -sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$TASK_DIR/README.md" +sed -i "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$TASK_DIR/README.md" # Generate the validation doc OLD_SYS_CFG=$(cast impl "$SYSTEM_CONFIG_PROXY_ADDR") @@ -33,6 +33,6 @@ OLD_SYS_CFG=$(cast impl "$SYSTEM_CONFIG_PROXY_ADDR") PADDED_OLD_SYS_CFG=$(cast 2u "$OLD_SYS_CFG") PADDED_SYS_CFG=$(cast 2u "$SYSTEM_CONFIG_IMPL") -sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$OLD_SYS_CFG/$PADDED_OLD_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" -sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$PADDED_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$OLD_SYS_CFG/$PADDED_OLD_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" +sed -i "s/\$SYSTEM_CONFIG_IMPL/$PADDED_SYS_CFG/g" "$TASK_DIR/VALIDATION.md" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh index 64c34088ebf..bde4917e6b8 100755 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/scripts/sys-cfg-bundle.sh @@ -26,10 +26,10 @@ cp ./templates/sys_cfg_upgrade_bundle_template.json "$BUNDLE_PATH" TX_1_PAYLOAD=$(cast calldata "upgrade(address,address)" "$SYSTEM_CONFIG_PROXY_ADDR" "$SYSTEM_CONFIG_IMPL") # Replace variables -sed -i '' "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" -sed -i '' "s/\$PROXY_ADMIN_ADDR/$PROXY_ADMIN_ADDR/g" "$BUNDLE_PATH" -sed -i '' "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$BUNDLE_PATH" -sed -i '' "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$BUNDLE_PATH" -sed -i '' "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" +sed -i "s/\$L1_CHAIN_ID/$L1_CHAIN_ID/g" "$BUNDLE_PATH" +sed -i "s/\$PROXY_ADMIN_ADDR/$PROXY_ADMIN_ADDR/g" "$BUNDLE_PATH" +sed -i "s/\$SYSTEM_CONFIG_PROXY_ADDR/$SYSTEM_CONFIG_PROXY_ADDR/g" "$BUNDLE_PATH" +sed -i "s/\$SYSTEM_CONFIG_IMPL/$SYSTEM_CONFIG_IMPL/g" "$BUNDLE_PATH" +sed -i "s/\$TX_1_PAYLOAD/$TX_1_PAYLOAD/g" "$BUNDLE_PATH" echo "✨ Generated SystemConfig upgrade bundle at \"$BUNDLE_PATH\"" diff --git a/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile index f91ff8dadd8..33166fe6e5a 100644 --- a/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile +++ b/packages/contracts-bedrock/scripts/upgrades/holocene/upgrade.dockerfile @@ -58,5 +58,8 @@ RUN forge script ./scripts/upgrades/holocene/DeployUpgrade.s.sol || true # Set the working directory to where upgrade.sh is located WORKDIR /app/packages/contracts-bedrock/scripts/upgrades/holocene +# allows to use modified local scripts +COPY scripts/*.sh ./scripts/ + # Set the entrypoint to the main.sh script ENTRYPOINT ["./scripts/main.sh"] diff --git a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json index 6434233930d..9f51cc1c3a2 100644 --- a/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json +++ b/packages/contracts-bedrock/snapshots/abi/L2ToL2CrossDomainMessenger.json @@ -247,6 +247,11 @@ "name": "IdOriginNotL2ToL2CrossDomainMessenger", "type": "error" }, + { + "inputs": [], + "name": "InvalidChainId", + "type": "error" + }, { "inputs": [], "name": "MessageAlreadyRelayed", diff --git a/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json b/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json index 24ac8a88fab..fbe697b864d 100644 --- a/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json +++ b/packages/contracts-bedrock/snapshots/abi/OptimismSuperchainERC20.json @@ -454,6 +454,12 @@ "internalType": "uint256", "name": "amount", "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" } ], "name": "CrosschainBurn", @@ -473,6 +479,12 @@ "internalType": "uint256", "name": "amount", "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" } ], "name": "CrosschainMint", diff --git a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json index 32d84df7d88..e8df09a8064 100644 --- a/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json +++ b/packages/contracts-bedrock/snapshots/abi/SuperchainWETH.json @@ -143,6 +143,53 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "relayETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_chainId", + "type": "uint256" + } + ], + "name": "sendETH", + "outputs": [ + { + "internalType": "bytes32", + "name": "msgHash_", + "type": "bytes32" + } + ], + "stateMutability": "payable", + "type": "function" + }, { "inputs": [ { @@ -306,6 +353,12 @@ "internalType": "uint256", "name": "amount", "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" } ], "name": "CrosschainBurn", @@ -325,6 +378,12 @@ "internalType": "uint256", "name": "amount", "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" } ], "name": "CrosschainMint", @@ -349,6 +408,68 @@ "name": "Deposit", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "source", + "type": "uint256" + } + ], + "name": "RelayETH", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "destination", + "type": "uint256" + } + ], + "name": "SendETH", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -393,6 +514,11 @@ "name": "Withdrawal", "type": "event" }, + { + "inputs": [], + "name": "InvalidCrossDomainSender", + "type": "error" + }, { "inputs": [], "name": "NotCustomGasToken", @@ -402,5 +528,10 @@ "inputs": [], "name": "Unauthorized", "type": "error" + }, + { + "inputs": [], + "name": "ZeroAddress", + "type": "error" } ] \ No newline at end of file diff --git a/packages/contracts-bedrock/snapshots/semver-lock.json b/packages/contracts-bedrock/snapshots/semver-lock.json index b3296d0a5d7..889a15db750 100644 --- a/packages/contracts-bedrock/snapshots/semver-lock.json +++ b/packages/contracts-bedrock/snapshots/semver-lock.json @@ -20,8 +20,8 @@ "sourceCodeHash": "0x4132ff37d267cb12224b75ea806c0aa7d25407b0d66ce526d7fcda8f7d223882" }, "src/L1/OPContractsManager.sol": { - "initCodeHash": "0xd038cc35325d023499151264232d75fa4ecc81f04a8c8353e6b50c43af224d6e", - "sourceCodeHash": "0xa13f3ab2b8744015290dbabe5f20fdd44774607e6a7ad3e5e016303fc4aa8c12" + "initCodeHash": "0xdc41db13327e1f51ce996c088538c227a82c37d62906eca1aa456d71fa605ff3", + "sourceCodeHash": "0x08aca8a70f42a0556f0fc7b1cd4d4aaf152215ab246c132e23074558497fe03b" }, "src/L1/OptimismPortal.sol": { "initCodeHash": "0x152167cfa18635ae4918a6eb3371a599cfa084418c0a652799cdb48bfc0ee0cc", @@ -32,36 +32,36 @@ "sourceCodeHash": "0xb7cbcb27240d0fd85a7a3009676abb93679e3b8723a967c1f310390464399869" }, "src/L1/OptimismPortalInterop.sol": { - "initCodeHash": "0x831af803158768b7fc229822eebda0ce7922cb1852fd770856b425023a379d47", - "sourceCodeHash": "0xe1dbe5e95ffed6587126ea65a5e4d3c8f11e1bd17a194a5665c7f0c0d32f29e3" + "initCodeHash": "0xbcd9d2a47e2b6076d242762cc072387d9da5532b72d9f0286de2d683cd918b13", + "sourceCodeHash": "0xd3f70c69c35d05f0eec20ac7dae4d86b20066af2b2395387cc62ad80b5a27b06" }, "src/L1/ProtocolVersions.sol": { - "initCodeHash": "0xefd4806e8737716d5d2022ca2e9e9fba0a0cb5714b026166b58e472222c7d15f", - "sourceCodeHash": "0x15205131bf420aa6d03c558bb75dd49cd7439caed7ccdcbfd89c4170a48c94f5" + "initCodeHash": "0x98dc9a7ecd1919a8cf4bcca4fb28b112641b2aa5132fb5467a349fb80974957a", + "sourceCodeHash": "0x9b953cdbc049492d20a15d48a9081977a91f8e7c72eee3792d86871e70b1a40a" }, "src/L1/SharedLockbox.sol": { "initCodeHash": "0xa6aa965a99138876c2ba0c48910068ea0d0133699a2a22e7dc867a46d9e927af", "sourceCodeHash": "0x26b7450f991fe197a94861cf618df04feb695dbbba884ffe93f96defa63e5bd7" }, "src/L1/SuperchainConfig.sol": { - "initCodeHash": "0x5cd6b119f03beea59e2ba29ea92694e6e5cbcbb1a83a02557ffbe332ce687ab6", - "sourceCodeHash": "0x1e191c7746b936427b8391c68a42b542eab0821c005a4a6a517bbe855278cced" + "initCodeHash": "0x56780fdb911f250dde8a5cfb2aae158405503cf295efd784ba9823667838bd99", + "sourceCodeHash": "0x3693d009d1d02508adec63bcb6bf2e7a6ec5f8255ddff8ffdcb792fae0cd4b84" }, "src/L1/SystemConfig.sol": { "initCodeHash": "0x0eda38e2fb2687a324289f04ec8ad0d2afe51f45219d074740fb4a0e24ea6569", "sourceCodeHash": "0x6dbbe8716ca8cd2fba3da8dcae0ca0c4b1f3e9dd04220fb24a15666b23300927" }, "src/L1/SystemConfigInterop.sol": { - "initCodeHash": "0x9376acb45613d5042ac316f3dd0fa09e7ea1f7b9e99a879b81a8cc31185ed3c7", - "sourceCodeHash": "0x40d76b65fa9074a6539de9a73f0735badb1fc1fe0a0b1b42829065ba6376b176" + "initCodeHash": "0x1d122143f63b5c00af41a246b1099ee968af04df7af394db874613d80bc20898", + "sourceCodeHash": "0x2e928783bce90409817c223e6746b74f4c164d614bcf0b0f26a1b82811882974" }, "src/L2/BaseFeeVault.sol": { - "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", - "sourceCodeHash": "0x983e8e248c61e362ba6a01dd2e217a535c9bb828dc0b4421f5f27e0577f2e14c" + "initCodeHash": "0xc23d0437deb5c2e9f8831b9325c77e9d07467c9286f81c0ef47cfef4e242f96b", + "sourceCodeHash": "0x2f3f64d4307a081311222b34afa76e587aca3789367f6ea77bc1bfb6d9b39563" }, "src/L2/CrossL2Inbox.sol": { - "initCodeHash": "0x31ecaebf368ab3333e80c6dc004b3c9f9a31f813c3138ab388bb3eead9f1b4ee", - "sourceCodeHash": "0xa1779d84a14332dcdd167293171d0fe2629d759a23d7cc34ffe2bde7e1605dbc" + "initCodeHash": "0x753a7685c4ab405b71a19a7f8aa20a2517b4943bee5d5e616a6fccd8f0531993", + "sourceCodeHash": "0xc74277f9502338def330444413ff77157e200110cd7f57cd9a18f9ffe4a18a6c" }, "src/L2/ETHLiquidity.sol": { "initCodeHash": "0x713c18f95a6a746d0703f475f3ae10c106c9b9ecb64d881a2e61b8969b581371", @@ -72,16 +72,16 @@ "sourceCodeHash": "0xa12ce15ded3cca681b2fc9facaebbb45d740dd6f9c9496333c1c46689c9a2d99" }, "src/L2/L1Block.sol": { - "initCodeHash": "0xa919d2aa76a7ecdfd076e2b1dbece499cc85706075f16eb6fa7b1a0fa7b38c1b", - "sourceCodeHash": "0x692cfcbc06dba6328f6e5c6b500741df04e4bdf730b2069aeb5d168355ea7b6f" + "initCodeHash": "0xb25ff86c5b199f0e4db9f814f3d107cd929278a39e838c541af79d49d472b646", + "sourceCodeHash": "0xa44964dc0178949b83fc4100c03933c6d6f66e9654ce6b4aff5847a889a189d4" }, "src/L2/L1BlockInterop.sol": { - "initCodeHash": "0x62e9cc59daaf72066ac20597a666db33e9a7b3f7be71a3d47ea4841a9aca9d07", - "sourceCodeHash": "0xe57627347366d74029a0d24f0b45d7b9cf82b81c94681d0f633d5e5c37c8de4a" + "initCodeHash": "0xb59a6a62926084b9979e337183ee54b4c4e9ceeda7e39bd70bd7b4228464d2e0", + "sourceCodeHash": "0x5681bb385bf1685cb204a612ef8a891e91763e699c7738a3a97b171852d585b5" }, "src/L2/L1FeeVault.sol": { - "initCodeHash": "0xbf49824cf37e201181484a8a423fcad8f504dc925921a2b28e83398197858dec", - "sourceCodeHash": "0xc7cda130f2bb3648e04d5a480082aa1789e16456c1280954d822b05d30100b2d" + "initCodeHash": "0xc23d0437deb5c2e9f8831b9325c77e9d07467c9286f81c0ef47cfef4e242f96b", + "sourceCodeHash": "0x3f0acc4427d6f72debe4393c746b759c7c736f28b8c4f42391de622c9be3bcb3" }, "src/L2/L2CrossDomainMessenger.sol": { "initCodeHash": "0xc496495496b96ea0eaf417c5e56b295836c12db3e6aafe2e607563e7a50b5b65", @@ -92,72 +92,68 @@ "sourceCodeHash": "0xf8569c75b801f38f8a5a41e94e90f159ddc5f5412804b26e3e564755a50631b8" }, "src/L2/L2StandardBridge.sol": { - "initCodeHash": "0xcb4aa19f0cd43a35cb5c65f26c3cfd7c41f1d1e5bcc15aef6096d385df7272c9", - "sourceCodeHash": "0x89771b53b7f6e64d943afb2a4bf15395efcf20d5302b76a18e52fa7cce8cdc56" + "initCodeHash": "0x959ce0daeb14884127cff4d701b294fb29b5d7c2d890ec4fdf704c451164811e", + "sourceCodeHash": "0xd5f91687a178254714203c702afe0c2343c45c4684b914aa873dcb641e73e725" }, "src/L2/L2StandardBridgeInterop.sol": { - "initCodeHash": "0xc4eaece28d2cfca3c51247c3cce320a167a83c7fd13aea5736549d2b25e0b139", - "sourceCodeHash": "0x9e80044adf5f83c30b520ee153b75be5a152081c9e1271e7e618ecfccd1fb4ac" + "initCodeHash": "0x745f5e3a0deb6cd8fa5a339c83cc91f9ce63de570a7ed23e2da645bebaa79494", + "sourceCodeHash": "0x0ca6d8900a02c213acca44c8cb56df816fa1bebd6903a9ea6d4c21525c936c12" }, "src/L2/L2ToL1MessagePasser.sol": { - "initCodeHash": "0x13fe3729beb9ed966c97bef09acb9fe5043fe651d453145073d05f2567fa988d", - "sourceCodeHash": "0xd08a2e6514dbd44e16aa312a1b27b2841a9eab5622cbd05a39c30f543fad673c" + "initCodeHash": "0xe65929ad618380e7f005f042a0a9c8e662ad10d6e1c53bfb7f518a54b848fd79", + "sourceCodeHash": "0xd61a1face234756e99b7ab8383eef40e62833a91f727a77997d6f0e384526637" }, "src/L2/L2ToL2CrossDomainMessenger.sol": { - "initCodeHash": "0x2a1a1ee4f47175ce661ee8e4e50cfa879b082dcb5278b1d66ddda00ed77bb744", - "sourceCodeHash": "0xa76133db7f449ae742f9ba988ad86ccb5672475f61298b9fefe411b63b63e9f6" + "initCodeHash": "0x085c83fd07ce54b9d007e548008dd9d4c31d19cae9b9ca3aa1496191f84499fa", + "sourceCodeHash": "0x20d6a4f42b3169c17f938cfe479de7bd558c6ded816e1fcfddcdc5d86583b42b" }, "src/L2/OptimismSuperchainERC20.sol": { - "initCodeHash": "0x5bc5824030ecdb531e1f615d207cb73cdaa702e198769445d0ddbe717271eba9", - "sourceCodeHash": "0x0819c9411a155dca592d19b60c4176954202e4fe5d632a4ffbf88d465461252c" + "initCodeHash": "0x0e76b7ffa8af16f1bc5f74a55eb3ed64016228032b67c5f45afa7fd6fe4acf7e", + "sourceCodeHash": "0x92f370d0e167b7cec0deaded71d9c4a5a8303a2b921320123d3c32eeca1cc100" }, "src/L2/OptimismSuperchainERC20Beacon.sol": { - "initCodeHash": "0x23dba3ceb9e58646695c306996c9e15251ac79acc6339c1a93d10a4c79da6dab", - "sourceCodeHash": "0xf4379e49665823c877f5732f35068435ce06e2394fce6910a5e113d16cdc9f95" + "initCodeHash": "0xb032e99f5c205c8b474da89887e350277cdd05b99ee28374b97bfae18ef7a72c", + "sourceCodeHash": "0xc9ec1a022f82a3b463fad11d2cd6ade74bb0801ae797fdc0494840587fc595dd" }, "src/L2/OptimismSuperchainERC20Factory.sol": { - "initCodeHash": "0x18a362c57f08b611db98dfde96121385e938f995c84e3547c1c03fd49f9db2fd", - "sourceCodeHash": "0x450cd89d0aae7bbc85ff57a14a6d3468c24c6743f25943f6d895d34b1456c456" + "initCodeHash": "0xe6a098346699c3df248050fc3301197661d391f8e4849a93f00afeac17cae317", + "sourceCodeHash": "0x51980d07914ae48fd632aa54df24f3d6e2d335d0223a1d2ddd4ca5ced1797c16" }, "src/L2/SequencerFeeVault.sol": { - "initCodeHash": "0xcaadbf08057b5d47f7704257e9385a29e42a7a08c818646d109c5952d3d35218", - "sourceCodeHash": "0x05bbc6039e5a9ff38987e7b9b89c69e2ee8aa4b7ca20dd002ea1bbd3d70f27f3" + "initCodeHash": "0xf05acc7dcb80474e48ea36a3511bd4cbb35da065ed6280918b87a47c13a22cfc", + "sourceCodeHash": "0x3c0d09bd700b9db218445ecce76006574cabfe8c40c2156a6787c8a1360d7835" }, "src/L2/SuperchainERC20.sol": { "initCodeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", - "sourceCodeHash": "0xcf39c16893cace1e7d61350bfff05a27f3ce8da8eb0ac02cb5ac7bf603f163fa" + "sourceCodeHash": "0x4c40f0249c1c9871efd2e957dd9c769cc52b88d896385dd4d547f82eb06284be" }, "src/L2/SuperchainTokenBridge.sol": { "initCodeHash": "0x1cd2afdae6dd1b6ebc17f1d529e7d74c9b8b21b02db8589b8e389e2d5523d775", "sourceCodeHash": "0x617aa994f659c5d8ebd54128d994f86f5b175ceca095b024b8524a7898e8ae62" }, "src/L2/SuperchainWETH.sol": { - "initCodeHash": "0x5aef986a7c9c102b1e9b3068e2a2b66adce0a71dd5f39e03694622bf494f8d97", - "sourceCodeHash": "0xa62101a23b860e97f393027c898082a1c73d50679eceb6c6793844af29702359" + "initCodeHash": "0xac4cab1ee7f0a5de8362ddc37e1de92e816aee3fdd4e31c0ba0fd9b03568f0f0", + "sourceCodeHash": "0x1fc6439f7a5fac2e264d5e057593844d252cdaa12c2b1eaa2fb08cbb911a9f35" }, "src/L2/WETH.sol": { "initCodeHash": "0x17ea1b1c5d5a622d51c2961fde886a5498de63584e654ed1d69ee80dddbe0b17", "sourceCodeHash": "0x0fa0633a769e73f5937514c0003ba7947a1c275bbe5b85d78879c42f0ed8895b" }, - "src/asterisc/RISCV.sol": { - "initCodeHash": "0x6b4323061187f2c8efe8de43bf1ecdc0798e2d95ad69470ed4151dadc094fedf", - "sourceCodeHash": "0xd824f1ead87a1214fa8a4b435f493a80b7340ec2c959d2c1e1e9e8c062a42c4a" - }, "src/cannon/MIPS.sol": { - "initCodeHash": "0xa3cbf121bad13c00227ea4fef128853d9a86b7ec9158de894f99b58d38d7630a", - "sourceCodeHash": "0xd8467700c80b3e62fa37193dc6513bac35282094b686b50e162e157f704dde00" + "initCodeHash": "0xb4aec227019dacd6194d6aeb9ca68c23c60b95618d18a4ebc09243514aeb1f05", + "sourceCodeHash": "0x4d43b3f2918486aa76d2d59ac42e4f6aa2f58538c7e95a5cb99b63c9588b5f1c" }, "src/cannon/MIPS2.sol": { - "initCodeHash": "0xc38c76ab3aad78c81ca01b3235b402614972d6604b22fda1e870f1bf47be1194", - "sourceCodeHash": "0x3d38b1924669d1bde756f1306601c764a6d31f428ac72667a3dd194b3388210d" + "initCodeHash": "0xe3879b5772820d837bc1c77c32a1200eb26cf901d9302dff9f0e9759331e380e", + "sourceCodeHash": "0x1c45a8f4c8c9ded7043d63965cb114d17f801c6cd4d8233cb16838c5f9a02675" }, "src/cannon/MIPS64.sol": { - "initCodeHash": "0x93aa8d7f9fd3c22276c0d303a3fefdf8f73cc55807b35e483bba64c92d02aaef", - "sourceCodeHash": "0x171d66c651fdad2ac9c287da92689815a5b09589945ada092179508ad2326306" + "initCodeHash": "0xa4a761f480a26ec1926c5a8b4831440211c0441bd41d503b0aad189e030d35dc", + "sourceCodeHash": "0x7ddcf8584f9bd92abd1eb45bc198f5b0ec54acaf292f60e919d674cc56fb8abc" }, "src/cannon/PreimageOracle.sol": { - "initCodeHash": "0x5d7e8ae64f802bd9d760e3d52c0a620bd02405dc2c8795818db9183792ffe81c", - "sourceCodeHash": "0x979d8595d925c70a123e72c062fa58c9ef94777c2e93b6bc3231d6679e2e9055" + "initCodeHash": "0x2bef439027c37c65dd8e7d9a987ff14e1dba94ee5fe5f316a77ecf46a8db4b3f", + "sourceCodeHash": "0x70965927d1cfcafa8975f3fdc98a138efe413c4a1585c0f60e0d21cfd396cc7c" }, "src/dispute/AnchorStateRegistry.sol": { "initCodeHash": "0x7bdbf9dc5125c953ea1833ccf0ad0e07d25b6f6c47e23da5374413324a38c5f9", @@ -176,48 +172,52 @@ "sourceCodeHash": "0x730eff9147294c115a0a53e7e75771bcc4a517beb48457140ab929a8d1510893" }, "src/legacy/DeployerWhitelist.sol": { - "initCodeHash": "0x0b8177ed75b69eddbb9ce6537683f69a9935efed86a1d6faa8feaafbd151c1bd", - "sourceCodeHash": "0xc8fe9571fcf8fcb51a4dcb00ffa97f43a9ce811c323c4926e710b28c90a9005f" + "initCodeHash": "0xf232863fde5cd65368bcb4a79b41b5a4a09c59ede5070f82fd3f13f681bea7d8", + "sourceCodeHash": "0xd8d65492470907907d75574c1260fb349903af22c89098ea091439fc809310a9" }, "src/legacy/L1BlockNumber.sol": { "initCodeHash": "0x542955f1a84b304eaf291f76633b03e4c87c2654f7eff46c3bea94d27346ea1f", "sourceCodeHash": "0x898c239e6367a0971a075df18030a033cdada26983fa8a5cd6e7b88ec90d4958" }, "src/legacy/LegacyMessagePasser.sol": { - "initCodeHash": "0xefc6ed9e325c2d614ea0d28c3eabfff1b345f7c6054e90253c6a091c29508267", - "sourceCodeHash": "0xaa08a61448f485b277af57251d2089cc6a80ce0a763bf7184d48ffed5034ef69" + "initCodeHash": "0x11802672c929c4e92faeb7bee66617574b249b60a210f11ce29a5caa0e3d6bac", + "sourceCodeHash": "0x851ab8a2cd40798fce25698d4cfdb9dbf9c4c1e53f3cc8d1a325a6dd368807b0" }, "src/safe/DeputyGuardianModule.sol": { "initCodeHash": "0xd95e562f395d4eb6e332f4474dffab660ada9e9da7c79f58fb6052278e0904df", "sourceCodeHash": "0x45daabe094de0287e244e6fea4f1887b9adc09b07c47dc77361b1678645a1470" }, "src/safe/LivenessGuard.sol": { - "initCodeHash": "0x9ac0b039b1591f7c00cf11cb758d118c9b42e6e08250b619d6b6fd605a43d5ee", - "sourceCodeHash": "0xc1a968b0c6fbc4d82c2821c917b273feaaa224d258886b394416e84ee250d026" + "initCodeHash": "0xd449cef5d62dea387f5c4fd18382f5427532c443d29b3dcf0f645dbb6756ccb4", + "sourceCodeHash": "0x745f72d907d9a8e95b5fdfe9fd9a910456f2417c42b81b0a3a764c20c13d7ed7" }, "src/safe/LivenessModule.sol": { - "initCodeHash": "0xcfccdd9e423c95a0ddc6e09ccb6333d5fc8429ed2b8fc872f1290d392ae13aad", - "sourceCodeHash": "0xd1479c60087f352385b6d5379ef3cc07839f671d617626b4c94ece91da781ef2" + "initCodeHash": "0xb16fd6cdfe5125dbb065ab1b6fcd7a58665e9fc4907b1089c8a496576be5d4f7", + "sourceCodeHash": "0x17e70b1d4ffc876fde920eaeb5874ff8a39dfa03b1e24044dc73aff2820db1ef" }, "src/universal/OptimismMintableERC20.sol": { - "initCodeHash": "0x9cd677275b175812f1d5f90a127dbf7b3592714fd842a7a0de3988d716ca3eac", - "sourceCodeHash": "0x5611d8082f68af566554d7f09640b4b1f0e3efee4da1372b68fc7fc538a35ac7" + "initCodeHash": "0xc97c9502af8e83f1d9bd81f711c3d73db298dfdba465f463b3faa43a8208ed47", + "sourceCodeHash": "0x7e7ada21d7e5fb92521dad343ff71da9bdabd6e04882c81b8c8f74854f755ba6" }, "src/universal/OptimismMintableERC20Factory.sol": { - "initCodeHash": "0x03ad07bd7f89a29f1850fa8b5d377daf0e1d5aef6cb458a127df520549e8e8e6", - "sourceCodeHash": "0xdb6ec93782a4a217475195507740794a4f5553b9032e7ba31dc48b81f579a940" + "initCodeHash": "0x4ba75c8864565ac14aa213d2aacda58ce0f82b197fa72393328ac6968edb54fb", + "sourceCodeHash": "0xcd09be31be4e0fda6d329bd6ac6dccded3f029514be4908860db4d757ebb7bc7" }, "src/universal/OptimismMintableERC721.sol": { - "initCodeHash": "0x8aa309f2676d5267b6c9e411f88dc6e4badce414b8d66b330df3f60e9836380e", - "sourceCodeHash": "0x03bf7ad4d2b751bdead9930fc8f89b8e55d40dd4b2f5670fd339e87ae81f8b49" + "initCodeHash": "0xc7c5dbc9d19314f8ad00bd867a9186d04e7471502671c3daa23d90c1e3e16320", + "sourceCodeHash": "0x089ee309a0d9061646ab7a2781f80e5d94a74cc38fe808a3696e3984683d0a89" }, "src/universal/OptimismMintableERC721Factory.sol": { - "initCodeHash": "0x5ea977ba35558c3b75bebe28900548c763d205e40d6cf7660292b8e96bf3aea8", - "sourceCodeHash": "0x063ca3a0a2e3c592173af6157e383b5aaeff752000f98648a5c71260bb26590a" + "initCodeHash": "0xf62b7daa5e5005d989d9afb177c314868bef48641a7069ddf1d6c6656e064929", + "sourceCodeHash": "0x2ea30e241a348bfdd3dae41375b8a5db8c6eb609650f49a0945e6d453b5b6a41" }, "src/universal/StorageSetter.sol": { - "initCodeHash": "0x21b3059e9b13b330f76d02b61f61dcfa3abf3517a0b56afa0895c4b8291740bf", - "sourceCodeHash": "0xc1ea12a87e3a7ef9c950f0a41a4e35b60d4d9c4c816ff671dbfca663861c16f4" + "initCodeHash": "0x049f3c86965e575a370b14f7f49f3f15436ffb5ee1059615bb708659d7aae7de", + "sourceCodeHash": "0x5dc6b0b4ae4ab29085c52f74a4498d8a3d04928b844491749cd7186623e8b967" + }, + "src/vendor/asterisc/RISCV.sol": { + "initCodeHash": "0x6b4323061187f2c8efe8de43bf1ecdc0798e2d95ad69470ed4151dadc094fedf", + "sourceCodeHash": "0x26cae049cf171efcc84c946a400704c30ebec5dba4f1548d1f1529f68f56c1ec" }, "src/vendor/eas/EAS.sol": { "initCodeHash": "0xf96d1ebc530ed95e2dffebcfa2b4a1f18103235e6352d97838b77b7a2c14567b", diff --git a/packages/contracts-bedrock/src/L1/OPContractsManager.sol b/packages/contracts-bedrock/src/L1/OPContractsManager.sol index a2a5a5f215b..c0978e66cad 100644 --- a/packages/contracts-bedrock/src/L1/OPContractsManager.sol +++ b/packages/contracts-bedrock/src/L1/OPContractsManager.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { Blueprint } from "src/libraries/Blueprint.sol"; import { Constants } from "src/libraries/Constants.sol"; +import { Claim, Duration, GameType, GameTypes } from "src/dispute/lib/Types.sol"; +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; @@ -11,16 +14,12 @@ import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IAddressManager } from "src/legacy/interfaces/IAddressManager.sol"; - import { IProxyAdmin } from "src/universal/interfaces/IProxyAdmin.sol"; - import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol"; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IPermissionedDisputeGame } from "src/dispute/interfaces/IPermissionedDisputeGame.sol"; -import { Claim, Duration, GameType, GameTypes } from "src/dispute/lib/Types.sol"; - import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; import { IProtocolVersions } from "src/L1/interfaces/IProtocolVersions.sol"; import { IOptimismPortal2 } from "src/L1/interfaces/IOptimismPortal2.sol"; @@ -115,8 +114,8 @@ contract OPContractsManager is ISemver { // -------- Constants and Variables -------- - /// @custom:semver 1.0.0-beta.21 - string public constant version = "1.0.0-beta.21"; + /// @custom:semver 1.0.0-beta.22 + string public constant version = "1.0.0-beta.22"; /// @notice Represents the interface version so consumers know how to decode the DeployOutput struct /// that's emitted in the `Deployed` event. Whenever that struct changes, a new version should be used. diff --git a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol index a86e9440549..c22776ef3a1 100644 --- a/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/OptimismPortalInterop.sol @@ -3,13 +3,15 @@ pragma solidity 0.8.15; // Contracts import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { Constants } from "src/libraries/Constants.sol"; import { Unauthorized } from "src/libraries/PortalErrors.sol"; +// Interfaces +import { IL1BlockInterop, ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; + /// @custom:proxied true /// @title OptimismPortalInterop /// @notice The OptimismPortal is a low-level contract responsible for passing messages between L1 @@ -24,9 +26,9 @@ contract OptimismPortalInterop is OptimismPortal2 { OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds, _sharedLockbox) { } - /// @custom:semver +interop-beta.3 + /// @custom:semver +interop-beta.4 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.3"); + return string.concat(super.version(), "+interop-beta.4"); } /// @notice Sets static configuration options for the L2 system. @@ -49,7 +51,7 @@ contract OptimismPortalInterop is OptimismPortal2 { uint256(0), // value uint64(SYSTEM_DEPOSIT_GAS_LIMIT), // gasLimit false, // isCreation, - abi.encodeCall(L1BlockInterop.setConfig, (_type, _value)) + abi.encodeCall(IL1BlockInterop.setConfig, (_type, _value)) ) ); } diff --git a/packages/contracts-bedrock/src/L1/ProtocolVersions.sol b/packages/contracts-bedrock/src/L1/ProtocolVersions.sol index 8d4982ecea0..83fe4a90b63 100644 --- a/packages/contracts-bedrock/src/L1/ProtocolVersions.sol +++ b/packages/contracts-bedrock/src/L1/ProtocolVersions.sol @@ -1,10 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; + +// Libraries import { Storage } from "src/libraries/Storage.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @notice ProtocolVersion is a numeric identifier of the protocol version. type ProtocolVersion is uint256; @@ -36,8 +41,8 @@ contract ProtocolVersions is OwnableUpgradeable, ISemver { event ConfigUpdate(uint256 indexed version, UpdateType indexed updateType, bytes data); /// @notice Semantic version. - /// @custom:semver 1.0.1-beta.3 - string public constant version = "1.0.1-beta.3"; + /// @custom:semver 1.0.1-beta.4 + string public constant version = "1.0.1-beta.4"; /// @notice Constructs the ProtocolVersion contract. Cannot set /// the owner to `address(0)` due to the Ownable contract's diff --git a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol index d990ea04112..369b48a3398 100644 --- a/packages/contracts-bedrock/src/L1/SuperchainConfig.sol +++ b/packages/contracts-bedrock/src/L1/SuperchainConfig.sol @@ -1,14 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; + +// Libraries import { Storage } from "src/libraries/Storage.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol"; import { ISharedLockbox } from "src/L1/interfaces/ISharedLockbox.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @custom:proxied true /// @custom:audit none This contracts is not yet audited. /// @title SuperchainConfig @@ -57,8 +62,8 @@ contract SuperchainConfig is Initializable, ISemver { error ChainAlreadyAdded(); /// @notice Semantic version. - /// @custom:semver 1.1.1-beta.2 - string public constant version = "1.1.1-beta.2"; + /// @custom:semver 1.1.1-beta.3 + string public constant version = "1.1.1-beta.3"; // Mapping from chainId to SystemConfig address mapping(uint256 => address) public systemConfigs; diff --git a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol index 06ab21ef91d..c215404bd11 100644 --- a/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol +++ b/packages/contracts-bedrock/src/L1/SystemConfigInterop.sol @@ -3,9 +3,7 @@ pragma solidity 0.8.15; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { IOptimismPortalInterop as IOptimismPortal } from "src/L1/interfaces/IOptimismPortalInterop.sol"; import { SystemConfig } from "src/L1/SystemConfig.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; @@ -13,6 +11,10 @@ import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { StaticConfig } from "src/libraries/StaticConfig.sol"; import { Storage } from "src/libraries/Storage.sol"; +// Interfaces +import { IOptimismPortalInterop as IOptimismPortal } from "src/L1/interfaces/IOptimismPortalInterop.sol"; +import { ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; + /// @custom:proxied true /// @title SystemConfigInterop /// @notice The SystemConfig contract is used to manage configuration of an Optimism network. @@ -27,9 +29,9 @@ contract SystemConfigInterop is SystemConfig { /// @notice The address of the SuperchainConfig contract. address public immutable SUPERCHAIN_CONFIG; - /// @custom:semver +interop-beta.5 + /// @custom:semver +interop-beta.6 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.5"); + return string.concat(super.version(), "+interop-beta.6"); } /// @notice Constructs the SystemConfig contract. diff --git a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol index 276b9cea14b..9b8458a9faa 100644 --- a/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol +++ b/packages/contracts-bedrock/src/L1/interfaces/IOptimismPortalInterop.sol @@ -7,7 +7,7 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISuperchainConfig } from "src/L1/interfaces/ISuperchainConfig.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; +import { ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; interface IOptimismPortalInterop { error AlreadyFinalized(); diff --git a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol index 2fd33b9290b..4dd6a87023b 100644 --- a/packages/contracts-bedrock/src/L2/BaseFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/BaseFeeVault.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; +// Contracts import { FeeVault } from "src/L2/FeeVault.sol"; +// Libraries import { Types } from "src/libraries/Types.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000019 /// @title BaseFeeVault /// @notice The BaseFeeVault accumulates the base fee that is paid by transactions. contract BaseFeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; /// @notice Constructs the BaseFeeVault contract. /// @param _recipient Wallet that will receive the fees. diff --git a/packages/contracts-bedrock/src/L2/CrossDomainOwnable.sol b/packages/contracts-bedrock/src/L2/CrossDomainOwnable.sol index 3b532f58900..658cfc50f8b 100644 --- a/packages/contracts-bedrock/src/L2/CrossDomainOwnable.sol +++ b/packages/contracts-bedrock/src/L2/CrossDomainOwnable.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Contracts import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +// Libraries import { AddressAliasHelper } from "src/vendor/AddressAliasHelper.sol"; /// @title CrossDomainOwnable diff --git a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol index b330dfb0ceb..ca9129f710a 100644 --- a/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol +++ b/packages/contracts-bedrock/src/L2/CrossL2Inbox.sol @@ -1,10 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +// Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { TransientContext, TransientReentrancyAware } from "src/libraries/TransientContext.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; + +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IDependencySet } from "src/L2/interfaces/IDependencySet.sol"; import { IL1BlockInterop } from "src/L2/interfaces/IL1BlockInterop.sol"; @@ -73,8 +76,8 @@ contract CrossL2Inbox is ISemver, TransientReentrancyAware { address internal constant DEPOSITOR_ACCOUNT = 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001; /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.10 + string public constant version = "1.0.0-beta.10"; /// @notice Emitted when a cross chain message is being executed. /// @param msgHash Hash of message payload being executed. diff --git a/packages/contracts-bedrock/src/L2/L1Block.sol b/packages/contracts-bedrock/src/L2/L1Block.sol index 0b2ffd2c578..1f7a935339a 100644 --- a/packages/contracts-bedrock/src/L2/L1Block.sol +++ b/packages/contracts-bedrock/src/L2/L1Block.sol @@ -1,11 +1,14 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; +// Libraries import { Constants } from "src/libraries/Constants.sol"; import { GasPayingToken, IGasToken } from "src/libraries/GasPayingToken.sol"; import { NotDepositor } from "src/libraries/L1BlockErrors.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000015 /// @title L1Block @@ -57,9 +60,9 @@ contract L1Block is ISemver, IGasToken { /// @notice The latest L1 blob base fee. uint256 public blobBaseFee; - /// @custom:semver 1.5.1-beta.3 + /// @custom:semver 1.5.1-beta.4 function version() public pure virtual returns (string memory) { - return "1.5.1-beta.3"; + return "1.5.1-beta.4"; } /// @notice Returns the gas paying token, its decimals, name and symbol. diff --git a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol index 2cf6bd96c7d..b3a5cf51be1 100644 --- a/packages/contracts-bedrock/src/L2/L1BlockInterop.sol +++ b/packages/contracts-bedrock/src/L2/L1BlockInterop.sol @@ -49,9 +49,9 @@ contract L1BlockInterop is L1Block { /// keccak256(abi.encode(uint256(keccak256("l1Block.identifier.isDeposit")) - 1)) & ~bytes32(uint256(0xff)) uint256 internal constant IS_DEPOSIT_SLOT = 0x921bd3a089295c6e5540e8fba8195448d253efd6f2e3e495b499b627dc36a300; - /// @custom:semver +interop-beta.1 + /// @custom:semver +interop-beta.2 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.1"); + return string.concat(super.version(), "+interop-beta.2"); } /// @notice Returns whether the call was triggered from a a deposit or not. diff --git a/packages/contracts-bedrock/src/L2/L1FeeVault.sol b/packages/contracts-bedrock/src/L2/L1FeeVault.sol index c80c40b9849..3e69806ce53 100644 --- a/packages/contracts-bedrock/src/L2/L1FeeVault.sol +++ b/packages/contracts-bedrock/src/L2/L1FeeVault.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; +// Contracts import { FeeVault } from "src/L2/FeeVault.sol"; +// Libraries import { Types } from "src/libraries/Types.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @custom:proxied true /// @custom:predeploy 0x420000000000000000000000000000000000001A /// @title L1FeeVault /// @notice The L1FeeVault accumulates the L1 portion of the transaction fees. contract L1FeeVault is FeeVault, ISemver { /// @notice Semantic version. - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; /// @notice Constructs the L1FeeVault contract. /// @param _recipient Wallet that will receive the fees. diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol index 63bda3209fb..d2cb6f81556 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridge.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridge.sol @@ -3,7 +3,6 @@ pragma solidity 0.8.15; // Contracts import { StandardBridge } from "src/universal/StandardBridge.sol"; -import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; @@ -11,6 +10,7 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; +import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; /// @custom:proxied true @@ -58,9 +58,9 @@ contract L2StandardBridge is StandardBridge, ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.11.1-beta.3 + /// @custom:semver 1.11.1-beta.4 function version() public pure virtual returns (string memory) { - return "1.11.1-beta.3"; + return "1.11.1-beta.4"; } /// @notice Constructs the L2StandardBridge contract. diff --git a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol index e17ef29dd96..622d9218723 100644 --- a/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol +++ b/packages/contracts-bedrock/src/L2/L2StandardBridgeInterop.sol @@ -40,9 +40,9 @@ contract L2StandardBridgeInterop is L2StandardBridge { event Converted(address indexed from, address indexed to, address indexed caller, uint256 amount); /// @notice Semantic version. - /// @custom:semver +interop-beta.2 + /// @custom:semver +interop-beta.3 function version() public pure override returns (string memory) { - return string.concat(super.version(), "+interop-beta.2"); + return string.concat(super.version(), "+interop-beta.3"); } /// @notice Converts `amount` of `from` token to `to` token. diff --git a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol index 94b8213983e..9492b3fada1 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL1MessagePasser.sol @@ -1,10 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { Types } from "src/libraries/Types.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Encoding } from "src/libraries/Encoding.sol"; import { Burn } from "src/libraries/Burn.sol"; + +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:proxied true @@ -48,8 +51,8 @@ contract L2ToL1MessagePasser is ISemver { /// @param amount Amount of ETh that was burned. event WithdrawerBalanceBurnt(uint256 indexed amount); - /// @custom:semver 1.1.1-beta.1 - string public constant version = "1.1.1-beta.1"; + /// @custom:semver 1.1.1-beta.2 + string public constant version = "1.1.1-beta.2"; /// @notice Allows users to withdraw ETH by sending directly to this contract. receive() external payable { diff --git a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol index 6b1d7327dbc..731d36c9787 100644 --- a/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/L2ToL2CrossDomainMessenger.sol @@ -1,14 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +// Libraries import { Encoding } from "src/libraries/Encoding.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { CrossL2Inbox, Identifier } from "src/L2/CrossL2Inbox.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { TransientReentrancyAware } from "src/libraries/TransientContext.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IDependencySet } from "src/L2/interfaces/IDependencySet.sol"; +import { ICrossL2Inbox, Identifier } from "src/L2/interfaces/ICrossL2Inbox.sol"; + /// @notice Thrown when a non-written slot in transient storage is attempted to be read from. error NotEntered(); @@ -39,6 +43,9 @@ error ReentrantCall(); /// @notice Thrown when a call to the target contract during message relay fails. error TargetCallFailed(); +/// @notice Thrown when attempting to use a chain ID that is not in the dependency set. +error InvalidChainId(); + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000023 /// @title L2ToL2CrossDomainMessenger @@ -65,8 +72,8 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { uint16 public constant messageVersion = uint16(0); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.10 - string public constant version = "1.0.0-beta.10"; + /// @custom:semver 1.0.0-beta.12 + string public constant version = "1.0.0-beta.12"; /// @notice Mapping of message hashes to boolean receipt values. Note that a message will only be present in this /// mapping if it has successfully been relayed on this chain, and can therefore not be relayed again. @@ -130,6 +137,7 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { if (_destination == block.chainid) revert MessageDestinationSameChain(); if (_target == Predeploys.CROSS_L2_INBOX) revert MessageTargetCrossL2Inbox(); if (_target == Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert MessageTargetL2ToL2CrossDomainMessenger(); + if (!IDependencySet(Predeploys.L1_BLOCK_ATTRIBUTES).isInDependencySet(_destination)) revert InvalidChainId(); uint256 nonce = messageNonce(); emit SentMessage(_destination, _target, nonce, msg.sender, _message); @@ -159,7 +167,7 @@ contract L2ToL2CrossDomainMessenger is ISemver, TransientReentrancyAware { } // Signal that this is a cross chain call that needs to have the identifier validated - CrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage)); + ICrossL2Inbox(Predeploys.CROSS_L2_INBOX).validateMessage(_id, keccak256(_sentMessage)); // Decode the payload (uint256 destination, address target, uint256 nonce, address sender, bytes memory message) = diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol index c323d8b7577..b39f544f47e 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; +// Contracts import { Initializable } from "@openzeppelin/contracts-v5/proxy/utils/Initializable.sol"; +import { SuperchainERC20 } from "src/L2/SuperchainERC20.sol"; + +// Libraries +import { Predeploys } from "src/libraries/Predeploys.sol"; import { ZeroAddress, Unauthorized } from "src/libraries/errors/CommonErrors.sol"; +// Interfaces +import { IOptimismSuperchainERC20 } from "src/L2/interfaces/IOptimismSuperchainERC20.sol"; + /// @custom:proxied true /// @title OptimismSuperchainERC20 /// @notice OptimismSuperchainERC20 is a standard extension of the base ERC20 token contract that unifies ERC20 token @@ -58,8 +63,8 @@ contract OptimismSuperchainERC20 is SuperchainERC20, Initializable { } /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.9 - string public constant override version = "1.0.0-beta.9"; + /// @custom:semver 1.0.0-beta.11 + string public constant override version = "1.0.0-beta.11"; /// @notice Constructs the OptimismSuperchainERC20 contract. constructor() { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol index e2b3dc437b0..3ac981f0742 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Beacon.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries +import { Predeploys } from "src/libraries/Predeploys.sol"; + +// Interfaces import { IBeacon } from "@openzeppelin/contracts/proxy/beacon/IBeacon.sol"; import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; /// @custom:proxied true /// @custom:predeployed 0x4200000000000000000000000000000000000027 @@ -11,8 +14,8 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; /// @notice OptimismSuperchainERC20Beacon is the beacon proxy for the OptimismSuperchainERC20 implementation. contract OptimismSuperchainERC20Beacon is IBeacon, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.2 - string public constant version = "1.0.0-beta.2"; + /// @custom:semver 1.0.0-beta.3 + string public constant version = "1.0.0-beta.3"; /// @inheritdoc IBeacon function implementation() external pure override returns (address) { diff --git a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol index 454e3b455d6..4639c72c68a 100644 --- a/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol +++ b/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20Factory.sol @@ -1,11 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; +// Contracts import { BeaconProxy } from "@openzeppelin/contracts-v5/proxy/beacon/BeaconProxy.sol"; +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; + +// Libraries import { CREATE3 } from "@rari-capital/solmate/src/utils/CREATE3.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; + +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:proxied /// @custom:predeployed 0x4200000000000000000000000000000000000026 @@ -22,8 +27,8 @@ contract OptimismSuperchainERC20Factory is ISemver { ); /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.4 - string public constant version = "1.0.0-beta.4"; + /// @custom:semver 1.0.0-beta.5 + string public constant version = "1.0.0-beta.5"; /// @notice Mapping of the deployed OptimismSuperchainERC20 to the remote token address. /// This is used to keep track of the token deployments. diff --git a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol index 69a78219e5b..71861c828f4 100644 --- a/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol +++ b/packages/contracts-bedrock/src/L2/SequencerFeeVault.sol @@ -1,19 +1,23 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; +// Contracts import { FeeVault } from "src/L2/FeeVault.sol"; +// Libraries import { Types } from "src/libraries/Types.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000011 /// @title SequencerFeeVault /// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during /// transaction processing and block production. contract SequencerFeeVault is FeeVault, ISemver { - /// @custom:semver 1.5.0-beta.3 - string public constant version = "1.5.0-beta.3"; + /// @custom:semver 1.5.0-beta.4 + string public constant version = "1.5.0-beta.4"; /// @notice Constructs the SequencerFeeVault contract. /// @param _recipient Wallet that will receive the fees. diff --git a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol index b9e6bbfbf78..10273d9bd36 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainERC20.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainERC20.sol @@ -1,21 +1,26 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; +// Contracts import { ERC20 } from "@solady-v0.0.245/tokens/ERC20.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +// Libraries +import { Predeploys } from "src/libraries/Predeploys.sol"; import { Unauthorized } from "src/libraries/errors/CommonErrors.sol"; +// Interfaces +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; + /// @title SuperchainERC20 /// @notice A standard ERC20 extension implementing IERC7802 for unified cross-chain fungibility across /// the Superchain. Allows the SuperchainTokenBridge to mint and burn tokens as needed. abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver { /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.5 + /// @custom:semver 1.0.0-beta.7 function version() external view virtual returns (string memory) { - return "1.0.0-beta.5"; + return "1.0.0-beta.7"; } /// @notice Allows the SuperchainTokenBridge to mint tokens. @@ -26,7 +31,7 @@ abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver { _mint(_to, _amount); - emit CrosschainMint(_to, _amount); + emit CrosschainMint(_to, _amount, msg.sender); } /// @notice Allows the SuperchainTokenBridge to burn tokens. @@ -37,7 +42,7 @@ abstract contract SuperchainERC20 is ERC20, IERC7802, ISemver { _burn(_from, _amount); - emit CrosschainBurn(_from, _amount); + emit CrosschainBurn(_from, _amount, msg.sender); } /// @inheritdoc IERC165 diff --git a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol index 29e179eba82..912f1aa3d86 100644 --- a/packages/contracts-bedrock/src/L2/SuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/SuperchainWETH.sol @@ -5,16 +5,18 @@ pragma solidity 0.8.15; import { WETH98 } from "src/universal/WETH98.sol"; // Libraries +import { NotCustomGasToken, Unauthorized, ZeroAddress } from "src/libraries/errors/CommonErrors.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; +import { SafeSend } from "src/universal/SafeSend.sol"; // Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; /// @custom:proxied true /// @custom:predeploy 0x4200000000000000000000000000000000000024 @@ -23,9 +25,26 @@ import { Unauthorized, NotCustomGasToken } from "src/libraries/errors/CommonErro /// within the superchain. SuperchainWETH can be converted into native ETH on chains that /// do not use a custom gas token. contract SuperchainWETH is WETH98, IERC7802, ISemver { + /// @notice Thrown when attempting to relay a message and the cross domain message sender is not SuperchainWETH. + error InvalidCrossDomainSender(); + + /// @notice Emitted when ETH is sent from one chain to another. + /// @param from Address of the sender. + /// @param to Address of the recipient. + /// @param amount Amount of ETH sent. + /// @param destination Chain ID of the destination chain. + event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination); + + /// @notice Emitted whenever ETH is successfully relayed on this chain. + /// @param from Address of the msg.sender of sendETH on the source chain. + /// @param to Address of the recipient. + /// @param amount Amount of ETH relayed. + /// @param source Chain ID of the source chain. + event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source); + /// @notice Semantic version. - /// @custom:semver 1.0.0-beta.10 - string public constant version = "1.0.0-beta.10"; + /// @custom:semver 1.0.0-beta.12 + string public constant version = "1.0.0-beta.12"; /// @inheritdoc WETH98 function deposit() public payable override { @@ -69,12 +88,13 @@ contract SuperchainWETH is WETH98, IERC7802, ISemver { _mint(_to, _amount); - // Mint from ETHLiquidity contract. + // Withdraw from ETHLiquidity contract. if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { + // NOTE: 'mint' will soon change to 'withdraw'. IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount); } - emit CrosschainMint(_to, _amount); + emit CrosschainMint(_to, _amount, msg.sender); } /// @notice Allows the SuperchainTokenBridge to burn tokens. @@ -85,12 +105,13 @@ contract SuperchainWETH is WETH98, IERC7802, ISemver { _burn(_from, _amount); - // Burn to ETHLiquidity contract. + // Deposit to ETHLiquidity contract. if (!IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { + // NOTE: 'burn' will soon change to 'deposit'. IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: _amount }(); } - emit CrosschainBurn(_from, _amount); + emit CrosschainBurn(_from, _amount, msg.sender); } /// @inheritdoc IERC165 @@ -98,4 +119,53 @@ contract SuperchainWETH is WETH98, IERC7802, ISemver { return _interfaceId == type(IERC7802).interfaceId || _interfaceId == type(IERC20).interfaceId || _interfaceId == type(IERC165).interfaceId; } + + /// @notice Sends ETH to some target address on another chain. + /// @param _to Address to send ETH to. + /// @param _chainId Chain ID of the destination chain. + /// @return msgHash_ Hash of the message sent. + function sendETH(address _to, uint256 _chainId) external payable returns (bytes32 msgHash_) { + if (_to == address(0)) revert ZeroAddress(); + + if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { + revert NotCustomGasToken(); + } + + // NOTE: 'burn' will soon change to 'deposit'. + IETHLiquidity(Predeploys.ETH_LIQUIDITY).burn{ value: msg.value }(); + + msgHash_ = IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).sendMessage({ + _destination: _chainId, + _target: address(this), + _message: abi.encodeCall(this.relayETH, (msg.sender, _to, msg.value)) + }); + + emit SendETH(msg.sender, _to, msg.value, _chainId); + } + + /// @notice Relays ETH received from another chain. + /// @param _from Address of the msg.sender of sendETH on the source chain. + /// @param _to Address to relay ETH to. + /// @param _amount Amount of ETH to relay. + function relayETH(address _from, address _to, uint256 _amount) external { + if (msg.sender != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER) revert Unauthorized(); + + (address crossDomainMessageSender, uint256 source) = + IL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER).crossDomainMessageContext(); + + if (crossDomainMessageSender != address(this)) revert InvalidCrossDomainSender(); + + if (IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES).isCustomGasToken()) { + // Since ETH is not the native asset on custom gas token chains, send SuperchainWETH to the recipient. + _mint(_to, _amount); + } else { + // NOTE: 'mint' will soon change to 'withdraw'. + IETHLiquidity(Predeploys.ETH_LIQUIDITY).mint(_amount); + + // This is a forced ETH send to the recipient, the recipient should NOT expect to be called. + new SafeSend{ value: _amount }(payable(_to)); + } + + emit RelayETH(_from, _to, _amount, source); + } } diff --git a/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol index 469230e822d..38c92d01d8d 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IERC7802.sol @@ -9,12 +9,14 @@ interface IERC7802 is IERC165 { /// @notice Emitted when a crosschain transfer mints tokens. /// @param to Address of the account tokens are being minted for. /// @param amount Amount of tokens minted. - event CrosschainMint(address indexed to, uint256 amount); + /// @param sender Address of the account that finilized the crosschain transfer. + event CrosschainMint(address indexed to, uint256 amount, address indexed sender); /// @notice Emitted when a crosschain transfer burns tokens. /// @param from Address of the account tokens are being burned from. /// @param amount Amount of tokens burned. - event CrosschainBurn(address indexed from, uint256 amount); + /// @param sender Address of the account that initiated the crosschain transfer. + event CrosschainBurn(address indexed from, uint256 amount, address indexed sender); /// @notice Mint tokens through a crosschain transfer. /// @param _to Address to mint tokens to. diff --git a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol index 00ca4906b5c..89311cf18f0 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol @@ -42,6 +42,9 @@ interface IL2ToL2CrossDomainMessenger { /// @notice Thrown when a call to the target contract during message relay fails. error TargetCallFailed(); + /// @notice Thrown when attempting to use a chain ID that is not in the dependency set. + error InvalidChainId(); + /// @notice Emitted whenever a message is sent to a destination /// @param destination Chain ID of the destination chain. /// @param target Target contract or wallet address. diff --git a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol index fa10b237dbb..4a4bbe35372 100644 --- a/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol +++ b/packages/contracts-bedrock/src/L2/interfaces/ISuperchainWETH.sol @@ -8,10 +8,18 @@ import { ISemver } from "src/universal/interfaces/ISemver.sol"; interface ISuperchainWETH is IWETH98, IERC7802, ISemver { error Unauthorized(); error NotCustomGasToken(); + error InvalidCrossDomainSender(); + error ZeroAddress(); + + event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination); + + event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source); function balanceOf(address src) external view returns (uint256); function withdraw(uint256 _amount) external; function supportsInterface(bytes4 _interfaceId) external view returns (bool); + function sendETH(address _to, uint256 _chainId) external payable returns (bytes32 msgHash_); + function relayETH(address _from, address _to, uint256 _amount) external; function __constructor__() external; } diff --git a/packages/contracts-bedrock/src/cannon/MIPS.sol b/packages/contracts-bedrock/src/cannon/MIPS.sol index 5efab0ce98f..62b3084f74a 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS.sol @@ -1,14 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol"; +// Libraries import { MIPSInstructions as ins } from "src/cannon/libraries/MIPSInstructions.sol"; import { MIPSSyscalls as sys } from "src/cannon/libraries/MIPSSyscalls.sol"; import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; import { MIPSMemory } from "src/cannon/libraries/MIPSMemory.sol"; import { InvalidRMWInstruction } from "src/cannon/libraries/CannonErrors.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; + /// @title MIPS /// @notice The MIPS contract emulates a single MIPS instruction. /// Note that delay slots are isolated instructions: @@ -44,8 +47,8 @@ contract MIPS is ISemver { } /// @notice The semantic version of the MIPS contract. - /// @custom:semver 1.2.1-beta.7 - string public constant version = "1.2.1-beta.7"; + /// @custom:semver 1.2.1-beta.8 + string public constant version = "1.2.1-beta.8"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS2.sol b/packages/contracts-bedrock/src/cannon/MIPS2.sol index 1d73473fde2..d28238980f3 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS2.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS2.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol"; +// Libraries import { MIPSMemory } from "src/cannon/libraries/MIPSMemory.sol"; import { MIPSSyscalls as sys } from "src/cannon/libraries/MIPSSyscalls.sol"; import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; @@ -12,6 +11,10 @@ import { InvalidMemoryProof, InvalidRMWInstruction, InvalidSecondMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; + /// @title MIPS2 /// @notice The MIPS2 contract emulates a single MIPS instruction. /// It differs from MIPS.sol in that it supports multi-threading. @@ -60,8 +63,8 @@ contract MIPS2 is ISemver { } /// @notice The semantic version of the MIPS2 contract. - /// @custom:semver 1.0.0-beta.22 - string public constant version = "1.0.0-beta.22"; + /// @custom:semver 1.0.0-beta.23 + string public constant version = "1.0.0-beta.23"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/MIPS64.sol b/packages/contracts-bedrock/src/cannon/MIPS64.sol index 53dd0649405..bb2f4369c2a 100644 --- a/packages/contracts-bedrock/src/cannon/MIPS64.sol +++ b/packages/contracts-bedrock/src/cannon/MIPS64.sol @@ -1,8 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { IPreimageOracle } from "./interfaces/IPreimageOracle.sol"; +// Libraries import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; import { MIPS64Syscalls as sys } from "src/cannon/libraries/MIPS64Syscalls.sol"; import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; @@ -13,6 +12,10 @@ import { InvalidMemoryProof, InvalidRMWInstruction, InvalidSecondMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; + /// @title MIPS64 /// @notice The MIPS64 contract emulates a single MIPS instruction. /// It differs from MIPS.sol in that it supports MIPS64 instructions and multi-tasking. @@ -64,8 +67,8 @@ contract MIPS64 is ISemver { } /// @notice The semantic version of the MIPS64 contract. - /// @custom:semver 1.0.0-beta.4 - string public constant version = "1.0.0-beta.4"; + /// @custom:semver 1.0.0-beta.5 + string public constant version = "1.0.0-beta.5"; /// @notice The preimage oracle contract. IPreimageOracle internal immutable ORACLE; diff --git a/packages/contracts-bedrock/src/cannon/PreimageOracle.sol b/packages/contracts-bedrock/src/cannon/PreimageOracle.sol index 7ac7c33d99d..03c0b3ae552 100644 --- a/packages/contracts-bedrock/src/cannon/PreimageOracle.sol +++ b/packages/contracts-bedrock/src/cannon/PreimageOracle.sol @@ -51,8 +51,8 @@ contract PreimageOracle is ISemver { uint256 public constant PRECOMPILE_CALL_RESERVED_GAS = 100_000; /// @notice The semantic version of the Preimage Oracle contract. - /// @custom:semver 1.1.3-beta.6 - string public constant version = "1.1.3-beta.6"; + /// @custom:semver 1.1.3-beta.7 + string public constant version = "1.1.3-beta.7"; //////////////////////////////////////////////////////////////// // Authorized Preimage Parts // diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol index d24ec036e81..7494135ace1 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Instructions.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; import { MIPS64Arch as arch } from "src/cannon/libraries/MIPS64Arch.sol"; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol index 9ed97396e10..f6e0633466e 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Memory.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; library MIPS64Memory { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol index 09678148f3e..09c347841b9 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64State.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol"; library MIPS64State { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol index 10954c64748..d4b706da4b5 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPS64Syscalls.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { MIPS64Memory } from "src/cannon/libraries/MIPS64Memory.sol"; import { MIPS64State as st } from "src/cannon/libraries/MIPS64State.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol index fa4d1451b54..f2af43da0fe 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSInstructions.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { MIPSMemory } from "src/cannon/libraries/MIPSMemory.sol"; import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol index 015955954b5..b78cc04a895 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSMemory.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { InvalidMemoryProof } from "src/cannon/libraries/CannonErrors.sol"; library MIPSMemory { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSState.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSState.sol index f9631e29e08..22fb608bcf5 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSState.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSState.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { InvalidExitedValue } from "src/cannon/libraries/CannonErrors.sol"; library MIPSState { diff --git a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol index ec0cfdded89..8e3516c0c1e 100644 --- a/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol +++ b/packages/contracts-bedrock/src/cannon/libraries/MIPSSyscalls.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { MIPSMemory } from "src/cannon/libraries/MIPSMemory.sol"; import { MIPSState as st } from "src/cannon/libraries/MIPSState.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; diff --git a/packages/contracts-bedrock/src/dispute/lib/Errors.sol b/packages/contracts-bedrock/src/dispute/lib/Errors.sol index 6e95bfa5a34..0dd4af18750 100644 --- a/packages/contracts-bedrock/src/dispute/lib/Errors.sol +++ b/packages/contracts-bedrock/src/dispute/lib/Errors.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; +// Libraries import { GameType, Hash, Claim } from "src/dispute/lib/LibUDT.sol"; //////////////////////////////////////////////////////////////// diff --git a/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol b/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol index bfc110f0952..780738a7974 100644 --- a/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol +++ b/packages/contracts-bedrock/src/dispute/lib/LibUDT.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; +// Libraries import { Position } from "src/dispute/lib/LibPosition.sol"; using LibClaim for Claim global; diff --git a/packages/contracts-bedrock/src/dispute/lib/Types.sol b/packages/contracts-bedrock/src/dispute/lib/Types.sol index 70df7b7912b..74106888fb0 100644 --- a/packages/contracts-bedrock/src/dispute/lib/Types.sol +++ b/packages/contracts-bedrock/src/dispute/lib/Types.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.15; +// Libraries import { Position, Hash, diff --git a/packages/contracts-bedrock/src/governance/GovernanceToken.sol b/packages/contracts-bedrock/src/governance/GovernanceToken.sol index 6fb1041555a..3843c3241b2 100644 --- a/packages/contracts-bedrock/src/governance/GovernanceToken.sol +++ b/packages/contracts-bedrock/src/governance/GovernanceToken.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; import { ERC20Votes, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol"; diff --git a/packages/contracts-bedrock/src/legacy/AddressManager.sol b/packages/contracts-bedrock/src/legacy/AddressManager.sol index daaac412e0a..e3b2e9274cc 100644 --- a/packages/contracts-bedrock/src/legacy/AddressManager.sol +++ b/packages/contracts-bedrock/src/legacy/AddressManager.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; /// @custom:legacy true diff --git a/packages/contracts-bedrock/src/legacy/DeployerWhitelist.sol b/packages/contracts-bedrock/src/legacy/DeployerWhitelist.sol index a7a6313edf4..e247f65ffb9 100644 --- a/packages/contracts-bedrock/src/legacy/DeployerWhitelist.sol +++ b/packages/contracts-bedrock/src/legacy/DeployerWhitelist.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:legacy true @@ -41,8 +42,8 @@ contract DeployerWhitelist is ISemver { } /// @notice Semantic version. - /// @custom:semver 1.1.1-beta.1 - string public constant version = "1.1.1-beta.1"; + /// @custom:semver 1.1.1-beta.2 + string public constant version = "1.1.1-beta.2"; /// @notice Adds or removes an address from the deployment whitelist. /// @param _deployer Address to update permissions for. diff --git a/packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol b/packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol index 28125a23f90..b6a5363e2fb 100644 --- a/packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol +++ b/packages/contracts-bedrock/src/legacy/L1ChugSplashProxy.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { Constants } from "src/libraries/Constants.sol"; + +// Interfaces import { IL1ChugSplashDeployer } from "src/legacy/interfaces/IL1ChugSplashProxy.sol"; /// @custom:legacy true diff --git a/packages/contracts-bedrock/src/legacy/LegacyMessagePasser.sol b/packages/contracts-bedrock/src/legacy/LegacyMessagePasser.sol index b90d1263f7a..d215f29bdc7 100644 --- a/packages/contracts-bedrock/src/legacy/LegacyMessagePasser.sol +++ b/packages/contracts-bedrock/src/legacy/LegacyMessagePasser.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @custom:legacy true @@ -14,8 +15,8 @@ contract LegacyMessagePasser is ISemver { mapping(bytes32 => bool) public sentMessages; /// @notice Semantic version. - /// @custom:semver 1.1.1-beta.1 - string public constant version = "1.1.1-beta.1"; + /// @custom:semver 1.1.1-beta.2 + string public constant version = "1.1.1-beta.2"; /// @notice Passes a message to L1. /// @param _message Message to pass to L1. diff --git a/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol b/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol index b3b58825824..8efd0bf0c49 100644 --- a/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol +++ b/packages/contracts-bedrock/src/legacy/LegacyMintableERC20.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +// Interfaces import { ILegacyMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; /// @title LegacyMintableERC20 diff --git a/packages/contracts-bedrock/src/legacy/ResolvedDelegateProxy.sol b/packages/contracts-bedrock/src/legacy/ResolvedDelegateProxy.sol index 3005456570f..d4e4a96f7ff 100644 --- a/packages/contracts-bedrock/src/legacy/ResolvedDelegateProxy.sol +++ b/packages/contracts-bedrock/src/legacy/ResolvedDelegateProxy.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { AddressManager } from "src/legacy/AddressManager.sol"; /// @custom:legacy true diff --git a/packages/contracts-bedrock/src/libraries/Arithmetic.sol b/packages/contracts-bedrock/src/libraries/Arithmetic.sol index dfd47274f7f..140affaa718 100644 --- a/packages/contracts-bedrock/src/libraries/Arithmetic.sol +++ b/packages/contracts-bedrock/src/libraries/Arithmetic.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Libraries import { SignedMath } from "@openzeppelin/contracts/utils/math/SignedMath.sol"; import { FixedPointMathLib } from "@rari-capital/solmate/src/utils/FixedPointMathLib.sol"; diff --git a/packages/contracts-bedrock/src/libraries/Constants.sol b/packages/contracts-bedrock/src/libraries/Constants.sol index 1cbd61d21a5..6a99410b5bc 100644 --- a/packages/contracts-bedrock/src/libraries/Constants.sol +++ b/packages/contracts-bedrock/src/libraries/Constants.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Interfaces import { IResourceMetering } from "src/L1/interfaces/IResourceMetering.sol"; /// @title Constants diff --git a/packages/contracts-bedrock/src/libraries/Encoding.sol b/packages/contracts-bedrock/src/libraries/Encoding.sol index 84d5f732f5f..5aa4ee7d3d8 100644 --- a/packages/contracts-bedrock/src/libraries/Encoding.sol +++ b/packages/contracts-bedrock/src/libraries/Encoding.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Libraries import { Types } from "src/libraries/Types.sol"; import { Hashing } from "src/libraries/Hashing.sol"; import { RLPWriter } from "src/libraries/rlp/RLPWriter.sol"; diff --git a/packages/contracts-bedrock/src/libraries/GasPayingToken.sol b/packages/contracts-bedrock/src/libraries/GasPayingToken.sol index 37c06840bd5..bf53367476b 100644 --- a/packages/contracts-bedrock/src/libraries/GasPayingToken.sol +++ b/packages/contracts-bedrock/src/libraries/GasPayingToken.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Libraries +import { LibString } from "@solady/utils/LibString.sol"; import { Storage } from "src/libraries/Storage.sol"; import { Constants } from "src/libraries/Constants.sol"; -import { LibString } from "@solady/utils/LibString.sol"; /// @title IGasToken /// @notice Implemented by contracts that are aware of the custom gas token used diff --git a/packages/contracts-bedrock/src/libraries/Hashing.sol b/packages/contracts-bedrock/src/libraries/Hashing.sol index 0f0f15678f9..b736ad9e4b7 100644 --- a/packages/contracts-bedrock/src/libraries/Hashing.sol +++ b/packages/contracts-bedrock/src/libraries/Hashing.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Libraries import { Types } from "src/libraries/Types.sol"; import { Encoding } from "src/libraries/Encoding.sol"; diff --git a/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol b/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol index 559c53391f7..f342658cb45 100644 --- a/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol +++ b/packages/contracts-bedrock/src/libraries/rlp/RLPReader.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.8; +// Libraries import { EmptyItem, UnexpectedString, @@ -8,7 +9,7 @@ import { ContentLengthMismatch, InvalidHeader, UnexpectedList -} from "./RLPErrors.sol"; +} from "src/libraries/rlp/RLPErrors.sol"; /// @custom:attribution https://github.com/hamdiallam/Solidity-RLP /// @title RLPReader diff --git a/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol b/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol index fa500894b16..cbbf9fdf3d6 100644 --- a/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol +++ b/packages/contracts-bedrock/src/libraries/trie/MerkleTrie.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Bytes } from "../Bytes.sol"; -import { RLPReader } from "../rlp/RLPReader.sol"; +// Libraries +import { Bytes } from "src/libraries/Bytes.sol"; +import { RLPReader } from "src/libraries/rlp/RLPReader.sol"; /// @title MerkleTrie /// @notice MerkleTrie is a small library for verifying standard Ethereum Merkle-Patricia trie diff --git a/packages/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol b/packages/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol index e8eab17917a..56992fba997 100644 --- a/packages/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol +++ b/packages/contracts-bedrock/src/libraries/trie/SecureMerkleTrie.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { MerkleTrie } from "./MerkleTrie.sol"; +// Libraries +import { MerkleTrie } from "src/libraries/trie/MerkleTrie.sol"; /// @title SecureMerkleTrie /// @notice SecureMerkleTrie is a thin wrapper around the MerkleTrie library that hashes the input diff --git a/packages/contracts-bedrock/src/periphery/AssetReceiver.sol b/packages/contracts-bedrock/src/periphery/AssetReceiver.sol index ce59d8ef753..0ee2d52a29b 100644 --- a/packages/contracts-bedrock/src/periphery/AssetReceiver.sol +++ b/packages/contracts-bedrock/src/periphery/AssetReceiver.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Contracts import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; -import { Transactor } from "./Transactor.sol"; +import { Transactor } from "src/periphery/Transactor.sol"; /// @title AssetReceiver /// @notice AssetReceiver is a minimal contract for receiving funds assets in the form of either diff --git a/packages/contracts-bedrock/src/periphery/Transactor.sol b/packages/contracts-bedrock/src/periphery/Transactor.sol index 01c8cc384ad..168367b4109 100644 --- a/packages/contracts-bedrock/src/periphery/Transactor.sol +++ b/packages/contracts-bedrock/src/periphery/Transactor.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; +// Contracts import { Owned } from "@rari-capital/solmate/src/auth/Owned.sol"; /// @title Transactor diff --git a/packages/contracts-bedrock/src/periphery/TransferOnion.sol b/packages/contracts-bedrock/src/periphery/TransferOnion.sol index db473a1828c..292eb9c3423 100644 --- a/packages/contracts-bedrock/src/periphery/TransferOnion.sol +++ b/packages/contracts-bedrock/src/periphery/TransferOnion.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +// Libraries import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /// @title TransferOnion diff --git a/packages/contracts-bedrock/src/periphery/drippie/Drippie.sol b/packages/contracts-bedrock/src/periphery/drippie/Drippie.sol index 014694813e4..c7240d015a9 100644 --- a/packages/contracts-bedrock/src/periphery/drippie/Drippie.sol +++ b/packages/contracts-bedrock/src/periphery/drippie/Drippie.sol @@ -1,8 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { AssetReceiver } from "../AssetReceiver.sol"; -import { IDripCheck } from "./IDripCheck.sol"; +// Contracts +import { AssetReceiver } from "src/periphery/AssetReceiver.sol"; + +// Interfaces +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; /// @title Drippie /// @notice Drippie is a system for managing automated contract interactions. A specific interaction diff --git a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckBalanceLow.sol b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckBalanceLow.sol index ff7121051c7..d889a4fa442 100644 --- a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckBalanceLow.sol +++ b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckBalanceLow.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDripCheck } from "../IDripCheck.sol"; +// Interfaces +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; /// @title CheckBalanceLow /// @notice DripCheck for checking if an account's balance is below a given threshold. diff --git a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckGelatoLow.sol b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckGelatoLow.sol index a5ef463f89e..7324e451c7d 100644 --- a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckGelatoLow.sol +++ b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckGelatoLow.sol @@ -1,8 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDripCheck } from "../IDripCheck.sol"; +// Interfaces import { IGelatoTreasury } from "src/vendor/interfaces/IGelatoTreasury.sol"; +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; /// @title CheckGelatoLow /// @notice DripCheck for checking if an account's Gelato ETH balance is below some threshold. diff --git a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckSecrets.sol b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckSecrets.sol index 5cfc4251b45..f255c2e6964 100644 --- a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckSecrets.sol +++ b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckSecrets.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDripCheck } from "../IDripCheck.sol"; +// Interfaces +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; /// @title CheckSecrets /// @notice DripCheck that checks if specific secrets exist (or not). Supports having a secret that diff --git a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckTrue.sol b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckTrue.sol index 1ce7138945c..18d4956f8e6 100644 --- a/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckTrue.sol +++ b/packages/contracts-bedrock/src/periphery/drippie/dripchecks/CheckTrue.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IDripCheck } from "../IDripCheck.sol"; +// Interfaces +import { IDripCheck } from "src/periphery/drippie/IDripCheck.sol"; /// @title CheckTrue /// @notice DripCheck that always returns true. diff --git a/packages/contracts-bedrock/src/periphery/faucet/Faucet.sol b/packages/contracts-bedrock/src/periphery/faucet/Faucet.sol index cce6a83b3e1..66cab547317 100644 --- a/packages/contracts-bedrock/src/periphery/faucet/Faucet.sol +++ b/packages/contracts-bedrock/src/periphery/faucet/Faucet.sol @@ -1,10 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IFaucetAuthModule } from "src/periphery/faucet/authmodules/IFaucetAuthModule.sol"; -import { SafeCall } from "src/libraries/SafeCall.sol"; +// Contracts import { SafeSend } from "src/universal/SafeSend.sol"; +// Libraries +import { SafeCall } from "src/libraries/SafeCall.sol"; + +// Interfaces +import { IFaucetAuthModule } from "src/periphery/faucet/authmodules/IFaucetAuthModule.sol"; + /// @title Faucet /// @notice Faucet contract that drips ETH to users. contract Faucet { diff --git a/packages/contracts-bedrock/src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol b/packages/contracts-bedrock/src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol index 115810d9d13..c9077943610 100644 --- a/packages/contracts-bedrock/src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol +++ b/packages/contracts-bedrock/src/periphery/faucet/authmodules/AdminFaucetAuthModule.sol @@ -1,10 +1,13 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol"; import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; -import { IFaucetAuthModule } from "./IFaucetAuthModule.sol"; -import { Faucet } from "../Faucet.sol"; + +// Interfaces +import { IFaucetAuthModule } from "src/periphery/faucet/authmodules/IFaucetAuthModule.sol"; +import { Faucet } from "src/periphery/faucet/Faucet.sol"; /// @title AdminFaucetAuthModule /// @notice FaucetAuthModule that allows an admin to sign off on a given faucet drip. Takes an admin diff --git a/packages/contracts-bedrock/src/periphery/faucet/authmodules/IFaucetAuthModule.sol b/packages/contracts-bedrock/src/periphery/faucet/authmodules/IFaucetAuthModule.sol index a94071dd2be..b4d91febf4a 100644 --- a/packages/contracts-bedrock/src/periphery/faucet/authmodules/IFaucetAuthModule.sol +++ b/packages/contracts-bedrock/src/periphery/faucet/authmodules/IFaucetAuthModule.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Faucet } from "../Faucet.sol"; +// Contracts +import { Faucet } from "src/periphery/faucet/Faucet.sol"; /// @title IFaucetAuthModule /// @notice Interface for faucet authentication modules. diff --git a/packages/contracts-bedrock/src/safe/LivenessGuard.sol b/packages/contracts-bedrock/src/safe/LivenessGuard.sol index aa9a231a4b2..f5966a9e146 100644 --- a/packages/contracts-bedrock/src/safe/LivenessGuard.sol +++ b/packages/contracts-bedrock/src/safe/LivenessGuard.sol @@ -1,12 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Safe import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; import { Guard as BaseGuard } from "safe-contracts/base/GuardManager.sol"; -import { SafeSigners } from "src/safe/SafeSigners.sol"; import { Enum } from "safe-contracts/common/Enum.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; + +// Libraries import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { SafeSigners } from "src/safe/SafeSigners.sol"; + +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title LivenessGuard /// @notice This Guard contract is used to track the liveness of Safe owners. @@ -25,8 +30,8 @@ contract LivenessGuard is ISemver, BaseGuard { event OwnerRecorded(address owner); /// @notice Semantic version. - /// @custom:semver 1.0.1-beta.2 - string public constant version = "1.0.1-beta.2"; + /// @custom:semver 1.0.1-beta.3 + string public constant version = "1.0.1-beta.3"; /// @notice The safe account for which this contract will be the guard. Safe internal immutable SAFE; diff --git a/packages/contracts-bedrock/src/safe/LivenessModule.sol b/packages/contracts-bedrock/src/safe/LivenessModule.sol index cd41c6e2dd5..50ab03d450c 100644 --- a/packages/contracts-bedrock/src/safe/LivenessModule.sol +++ b/packages/contracts-bedrock/src/safe/LivenessModule.sol @@ -1,10 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Safe import { GnosisSafe as Safe } from "safe-contracts/GnosisSafe.sol"; import { Enum } from "safe-contracts/common/Enum.sol"; import { OwnerManager } from "safe-contracts/base/OwnerManager.sol"; + +// Contracts import { LivenessGuard } from "src/safe/LivenessGuard.sol"; + +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title LivenessModule @@ -53,8 +58,8 @@ contract LivenessModule is ISemver { uint256 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8; /// @notice Semantic version. - /// @custom:semver 1.2.1-beta.1 - string public constant version = "1.2.1-beta.1"; + /// @custom:semver 1.2.1-beta.2 + string public constant version = "1.2.1-beta.2"; // Constructor to initialize the Safe and baseModule instances constructor( diff --git a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol index 66c724d3eba..85d801f0a1a 100644 --- a/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol +++ b/packages/contracts-bedrock/src/universal/CrossDomainMessenger.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; import { Hashing } from "src/libraries/Hashing.sol"; diff --git a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol index 52217fab713..3c6d7fe816f 100644 --- a/packages/contracts-bedrock/src/universal/ERC721Bridge.sol +++ b/packages/contracts-bedrock/src/universal/ERC721Bridge.sol @@ -1,10 +1,15 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +// Contracts import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +// Libraries +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; + +// Interfaces +import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; + /// @title ERC721Bridge /// @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges. abstract contract ERC721Bridge is Initializable { diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol index 34d601cbb65..0e25cd33938 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol @@ -1,13 +1,18 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; + +// Libraries +import { Preinstalls } from "src/libraries/Preinstalls.sol"; + +// Interfaces import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; +import { ISemver } from "src/universal/interfaces/ISemver.sol"; import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { Preinstalls } from "src/libraries/Preinstalls.sol"; /// @title OptimismMintableERC20 /// @notice OptimismMintableERC20 is a standard extension of the base ERC20 token contract designed @@ -42,8 +47,8 @@ contract OptimismMintableERC20 is ERC20Permit, ISemver { } /// @notice Semantic version. - /// @custom:semver 1.4.0-beta.2 - string public constant version = "1.4.0-beta.2"; + /// @custom:semver 1.4.0-beta.3 + string public constant version = "1.4.0-beta.3"; /// @notice Getter function for the permit2 address. It deterministically deployed /// so it will always be at the same address. It is also included as a preinstall, diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol index d7a9cd3372c..4f4b01602cc 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; + +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import { IOptimismERC20Factory } from "src/L2/interfaces/IOptimismERC20Factory.sol"; /// @custom:proxied true @@ -48,8 +51,8 @@ contract OptimismMintableERC20Factory is ISemver, Initializable, IOptimismERC20F /// the OptimismMintableERC20 token contract since this contract /// is responsible for deploying OptimismMintableERC20 contracts. /// @notice Semantic version. - /// @custom:semver 1.10.1-beta.4 - string public constant version = "1.10.1-beta.4"; + /// @custom:semver 1.10.1-beta.5 + string public constant version = "1.10.1-beta.5"; /// @notice Constructs the OptimismMintableERC20Factory contract. constructor() { diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol index 9dd05e10d1f..140c6ce5e6d 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721.sol @@ -1,11 +1,16 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; + +// Libraries import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; + +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IOptimismMintableERC721 } from "src/universal/interfaces/IOptimismMintableERC721.sol"; /// @title OptimismMintableERC721 /// @notice This contract is the remote representation for some token that lives on another network, @@ -41,8 +46,8 @@ contract OptimismMintableERC721 is ERC721Enumerable, ISemver { } /// @notice Semantic version. - /// @custom:semver 1.3.1-beta.3 - string public constant version = "1.3.1-beta.3"; + /// @custom:semver 1.3.1-beta.4 + string public constant version = "1.3.1-beta.4"; /// @param _bridge Address of the bridge on this network. /// @param _remoteChainId Chain ID where the remote token is deployed. diff --git a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol index 7350e0fae0d..da05e8f4f34 100644 --- a/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol +++ b/packages/contracts-bedrock/src/universal/OptimismMintableERC721Factory.sol @@ -1,7 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Contracts import { OptimismMintableERC721 } from "src/universal/OptimismMintableERC721.sol"; + +// Interfaces import { ISemver } from "src/universal/interfaces/ISemver.sol"; /// @title OptimismMintableERC721Factory @@ -25,8 +28,8 @@ contract OptimismMintableERC721Factory is ISemver { event OptimismMintableERC721Created(address indexed localToken, address indexed remoteToken, address deployer); /// @notice Semantic version. - /// @custom:semver 1.4.1-beta.4 - string public constant version = "1.4.1-beta.4"; + /// @custom:semver 1.4.1-beta.5 + string public constant version = "1.4.1-beta.5"; /// @notice The semver MUST be bumped any time that there is a change in /// the OptimismMintableERC721 token contract since this contract diff --git a/packages/contracts-bedrock/src/universal/Proxy.sol b/packages/contracts-bedrock/src/universal/Proxy.sol index b50b08efca8..4fd53dae06b 100644 --- a/packages/contracts-bedrock/src/universal/Proxy.sol +++ b/packages/contracts-bedrock/src/universal/Proxy.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; +// Libraries import { Constants } from "src/libraries/Constants.sol"; /// @title Proxy diff --git a/packages/contracts-bedrock/src/universal/StandardBridge.sol b/packages/contracts-bedrock/src/universal/StandardBridge.sol index 57af2247a65..dceb2fd4147 100644 --- a/packages/contracts-bedrock/src/universal/StandardBridge.sol +++ b/packages/contracts-bedrock/src/universal/StandardBridge.sol @@ -1,17 +1,21 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; +// Contracts +import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +// Libraries import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; +import { Constants } from "src/libraries/Constants.sol"; + +// Interfaces +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IOptimismMintableERC20 } from "src/universal/interfaces/IOptimismMintableERC20.sol"; import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; import { ICrossDomainMessenger } from "src/universal/interfaces/ICrossDomainMessenger.sol"; -import { OptimismMintableERC20 } from "src/universal/OptimismMintableERC20.sol"; -import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol"; -import { Constants } from "src/libraries/Constants.sol"; /// @custom:upgradeable /// @title StandardBridge @@ -294,7 +298,7 @@ abstract contract StandardBridge is Initializable { "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" ); - OptimismMintableERC20(_localToken).mint(_to, _amount); + IOptimismMintableERC20(_localToken).mint(_to, _amount); } else { deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] - _amount; IERC20(_localToken).safeTransfer(_to, _amount); @@ -364,7 +368,7 @@ abstract contract StandardBridge is Initializable { "StandardBridge: wrong remote token for Optimism Mintable ERC20 local token" ); - OptimismMintableERC20(_localToken).burn(_from, _amount); + IOptimismMintableERC20(_localToken).burn(_from, _amount); } else { IERC20(_localToken).safeTransferFrom(_from, address(this), _amount); deposits[_localToken][_remoteToken] = deposits[_localToken][_remoteToken] + _amount; diff --git a/packages/contracts-bedrock/src/universal/StorageSetter.sol b/packages/contracts-bedrock/src/universal/StorageSetter.sol index 5bd53a75b36..e6e4b5df545 100644 --- a/packages/contracts-bedrock/src/universal/StorageSetter.sol +++ b/packages/contracts-bedrock/src/universal/StorageSetter.sol @@ -1,9 +1,12 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -import { ISemver } from "src/universal/interfaces/ISemver.sol"; +// Libraries import { Storage } from "src/libraries/Storage.sol"; +// Interfaces +import { ISemver } from "src/universal/interfaces/ISemver.sol"; + /// @title StorageSetter /// @notice A simple contract that allows setting arbitrary storage slots. /// WARNING: this contract is not safe to be called by untrusted parties. @@ -16,8 +19,8 @@ contract StorageSetter is ISemver { } /// @notice Semantic version. - /// @custom:semver 1.2.1-beta.2 - string public constant version = "1.2.1-beta.2"; + /// @custom:semver 1.2.1-beta.3 + string public constant version = "1.2.1-beta.3"; /// @notice Stores a bytes32 `_value` at `_slot`. Any storage slots that /// are packed should be set through this interface. diff --git a/packages/contracts-bedrock/src/asterisc/RISCV.sol b/packages/contracts-bedrock/src/vendor/asterisc/RISCV.sol similarity index 99% rename from packages/contracts-bedrock/src/asterisc/RISCV.sol rename to packages/contracts-bedrock/src/vendor/asterisc/RISCV.sol index 8b0bbb5d1d7..09fa93a0fcd 100644 --- a/packages/contracts-bedrock/src/asterisc/RISCV.sol +++ b/packages/contracts-bedrock/src/vendor/asterisc/RISCV.sol @@ -8,7 +8,6 @@ import { IBigStepper } from "src/dispute/interfaces/IBigStepper.sol"; /// @notice The RISCV contract emulates a single RISCV hart cycle statelessly, using memory proofs to verify the /// instruction and optional memory access' inclusion in the memory merkle root provided in the trusted /// prestate witness. -/// This contract has been vendorized from the Asterisc project. The original source code can be found at /// @dev https://github.com/ethereum-optimism/asterisc contract RISCV is IBigStepper { /// @notice The preimage oracle contract. diff --git a/packages/contracts-bedrock/src/vendor/asterisc/interfaces/IRISCV.sol b/packages/contracts-bedrock/src/vendor/asterisc/interfaces/IRISCV.sol new file mode 100644 index 00000000000..349694fc876 --- /dev/null +++ b/packages/contracts-bedrock/src/vendor/asterisc/interfaces/IRISCV.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import { ISemver } from "src/universal/interfaces/ISemver.sol"; +import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; + +/// @title IRISCV +/// @notice Interface for the RISCV contract. +interface IRISCV is ISemver { + function oracle() external view returns (IPreimageOracle); + function step(bytes memory _stateData, bytes memory _proof, bytes32 _localContext) external returns (bytes32); + + function __constructor__(IPreimageOracle _oracle) external; +} diff --git a/packages/contracts-bedrock/src/vendor/eas/resolver/ISchemaResolver.sol b/packages/contracts-bedrock/src/vendor/eas/resolver/ISchemaResolver.sol index 51aac1ece23..d2089f83af8 100644 --- a/packages/contracts-bedrock/src/vendor/eas/resolver/ISchemaResolver.sol +++ b/packages/contracts-bedrock/src/vendor/eas/resolver/ISchemaResolver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import { Attestation } from "../Common.sol"; +import { Attestation } from "src/vendor/eas/Common.sol"; /// @title ISchemaResolver /// @notice The interface of an optional schema resolver. diff --git a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol index 490ae07c927..5719d3fa558 100644 --- a/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol +++ b/packages/contracts-bedrock/test/L1/L2OutputOracle.t.sol @@ -99,6 +99,31 @@ contract L2OutputOracle_getter_Test is L2OutputOracle_TestBase { l2OutputOracle.getL2Output(nextOutputIndex + 1); } + /// @dev Tests that `getL2OutputAfter` of an L2 block number returns the L2 output of the `getL2OutputIndexAfter` of + /// that block number. + function test_getL2OutputAfter_succeeds() external { + uint8 iterations = 5; + + Types.OutputProposal memory output; + Types.OutputProposal memory expectedOutput; + + for (uint8 i; i < iterations; i++) { + proposeAnotherOutput(); + } + + uint256 latestBlockNumber = l2OutputOracle.latestBlockNumber(); + for (uint8 i = iterations - 1; i > 0; i--) { + uint256 index = l2OutputOracle.getL2OutputIndexAfter(latestBlockNumber); + output = l2OutputOracle.getL2OutputAfter(latestBlockNumber); + expectedOutput = l2OutputOracle.getL2Output(index); + assertEq(output.outputRoot, expectedOutput.outputRoot); + assertEq(output.timestamp, expectedOutput.timestamp); + assertEq(output.l2BlockNumber, expectedOutput.l2BlockNumber); + + latestBlockNumber -= l2OutputOracle.SUBMISSION_INTERVAL(); + } + } + /// @dev Tests that `getL2OutputIndexAfter` returns the correct value /// when the input is the exact block number of the proposal. function test_getL2OutputIndexAfter_sameBlock_succeeds() external { diff --git a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol index bd6d3b5d7de..c39bf0ee0f7 100644 --- a/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/OptimismPortalInterop.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -// Testing utilities +// Testing import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries @@ -9,12 +9,8 @@ import { Constants } from "src/libraries/Constants.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import "src/libraries/PortalErrors.sol"; -// Target contract dependencies -import "src/libraries/PortalErrors.sol"; -import { OptimismPortalInterop } from "src/L1/OptimismPortalInterop.sol"; -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; - // Interfaces +import { IL1BlockInterop, ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; contract OptimismPortalInterop_Test is CommonTest { @@ -35,7 +31,7 @@ contract OptimismPortalInterop_Test is CommonTest { _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.SET_GAS_PAYING_TOKEN, _value)) }); vm.prank(address(_optimismPortalInterop().systemConfig())); @@ -58,7 +54,7 @@ contract OptimismPortalInterop_Test is CommonTest { _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.ADD_DEPENDENCY, _value)) }); vm.prank(address(_optimismPortalInterop().systemConfig())); @@ -81,7 +77,7 @@ contract OptimismPortalInterop_Test is CommonTest { _mint: 0, _gasLimit: 200_000, _isCreation: false, - _data: abi.encodeCall(L1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)) + _data: abi.encodeCall(IL1BlockInterop.setConfig, (ConfigType.REMOVE_DEPENDENCY, _value)) }); vm.prank(address(_optimismPortalInterop().systemConfig())); diff --git a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol index cb43b548a74..1b2c25c7627 100644 --- a/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol +++ b/packages/contracts-bedrock/test/L1/SystemConfigInterop.t.sol @@ -6,7 +6,6 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Contracts import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { ConfigType } from "src/L2/L1BlockInterop.sol"; // Libraries import { Constants } from "src/libraries/Constants.sol"; @@ -17,6 +16,7 @@ import { GasPayingToken } from "src/libraries/GasPayingToken.sol"; import { ISystemConfig } from "src/L1/interfaces/ISystemConfig.sol"; import { ISystemConfigInterop } from "src/L1/interfaces/ISystemConfigInterop.sol"; import { IOptimismPortalInterop } from "src/L1/interfaces/IOptimismPortalInterop.sol"; +import { ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; contract SystemConfigInterop_Test is CommonTest { /// @notice Marked virtual to be overridden in diff --git a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol index 0a1d97c9351..f013325ef7e 100644 --- a/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol +++ b/packages/contracts-bedrock/test/L2/GasPriceOracle.t.sol @@ -117,6 +117,26 @@ contract GasPriceOracleBedrock_Test is GasPriceOracle_Test { vm.expectRevert("GasPriceOracle: Fjord can only be activated after Ecotone"); gasPriceOracle.setFjord(); } + + /// @dev Tests that `getL1Fee` returns the expected value when both fjord and ecotone are not active + function test_getL1Fee_whenFjordAndEcotoneNotActive_succeeds() external { + vm.store(address(gasPriceOracle), bytes32(uint256(0)), bytes32(0)); + bytes memory data = hex"1111"; + + uint256 price = gasPriceOracle.getL1Fee(data); + assertEq(price, 28_600); // ((((16 * data.length(i.e 2)) * (68 * 16)) + l1FeeOverhead(i.e. 310)) * + // l1BaseFee(i.e. 2M) * + // l1FeeScalar(i.e. 10)) / 1e6 + } + + /// @dev Tests that `getL1GasUsed` returns the expected value when both fjord and ecotone are not active + function test_getL1GasUsed_whenFjordAndEcotoneNotActive_succeeds() external { + vm.store(address(gasPriceOracle), bytes32(uint256(0)), bytes32(0)); + bytes memory data = hex"1111"; + + uint256 gas = gasPriceOracle.getL1GasUsed(data); + assertEq(gas, 1_430); // 1398 + (16 * data.length(i.e 2)) + } } contract GasPriceOracleEcotone_Test is GasPriceOracle_Test { diff --git a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol index c8779342c3b..01d3e6108f9 100644 --- a/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol +++ b/packages/contracts-bedrock/test/L2/L1BlockInterop.t.sol @@ -1,17 +1,17 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.15; -// Testing utilities +// Testing import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { StaticConfig } from "src/libraries/StaticConfig.sol"; - -// Target contract dependencies -import { L1BlockInterop, ConfigType } from "src/L2/L1BlockInterop.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import "src/libraries/L1BlockErrors.sol"; +// Interfaces +import { IL1BlockInterop, ConfigType } from "src/L2/interfaces/IL1BlockInterop.sol"; + contract L1BlockInteropTest is CommonTest { event GasPayingTokenSet(address indexed token, uint8 indexed decimals, bytes32 name, bytes32 symbol); event DependencyAdded(uint256 indexed chainId); @@ -199,8 +199,8 @@ contract L1BlockInteropTest is CommonTest { } /// @dev Returns the L1BlockInterop instance. - function _l1BlockInterop() internal view returns (L1BlockInterop) { - return L1BlockInterop(address(l1Block)); + function _l1BlockInterop() internal view returns (IL1BlockInterop) { + return IL1BlockInterop(address(l1Block)); } } @@ -261,7 +261,7 @@ contract L1BlockInteropSetL1BlockValuesInterop_Test is L1BlockInteropTest { vm.prank(_l1BlockInterop().DEPOSITOR_ACCOUNT()); (bool success,) = address(l1Block).call( - abi.encodePacked(L1BlockInterop.setL1BlockValuesInterop.selector, setValuesEcotoneCalldata) + abi.encodePacked(IL1BlockInterop.setL1BlockValuesInterop.selector, setValuesEcotoneCalldata) ); assertTrue(success, "function call failed"); diff --git a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol index 1e5e04edc25..fcfa375c1b5 100644 --- a/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/L2/L2ToL2CrossDomainMessenger.t.sol @@ -10,7 +10,6 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; import { Hashing } from "src/libraries/Hashing.sol"; // Target contract -import { CrossL2Inbox, Identifier } from "src/L2/CrossL2Inbox.sol"; import { L2ToL2CrossDomainMessenger, NotEntered, @@ -22,9 +21,14 @@ import { MessageTargetL2ToL2CrossDomainMessenger, MessageAlreadyRelayed, ReentrantCall, - TargetCallFailed + TargetCallFailed, + IDependencySet, + InvalidChainId } from "src/L2/L2ToL2CrossDomainMessenger.sol"; +// Interfaces +import { ICrossL2Inbox, Identifier } from "src/L2/interfaces/ICrossL2Inbox.sol"; + /// @title L2ToL2CrossDomainMessengerWithModifiableTransientStorage /// @dev L2ToL2CrossDomainMessenger contract with methods to modify the transient storage. /// This is used to test the transient storage of L2ToL2CrossDomainMessenger. @@ -85,6 +89,13 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure that the target contract is not CrossL2Inbox or L2ToL2CrossDomainMessenger vm.assume(_target != Predeploys.CROSS_L2_INBOX && _target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + // Mock the call over the `isInDependencySet` function to return true + vm.mockCall( + Predeploys.L1_BLOCK_ATTRIBUTES, + abi.encodeCall(IDependencySet.isInDependencySet, (_destination)), + abi.encode(true) + ); + // Get the current message nonce uint256 messageNonce = l2ToL2CrossDomainMessenger.messageNonce(); @@ -193,6 +204,34 @@ contract L2ToL2CrossDomainMessengerTest is Test { }); } + /// @notice Tests the `sendMessage` function reverts when the `destination` is not in the dependency set. + function testFuzz_sendMessage_notInDependencySet_reverts( + uint256 _destination, + address _target, + bytes calldata _message + ) + external + { + // Ensure the destination is not the same as the source, otherwise the function will revert + vm.assume(_destination != block.chainid); + + // Ensure that the target contract is not CrossL2Inbox or L2ToL2CrossDomainMessenger + vm.assume(_target != Predeploys.CROSS_L2_INBOX && _target != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + + // Mock the call over the `isInDependencySet` function to return false + vm.mockCall( + Predeploys.L1_BLOCK_ATTRIBUTES, + abi.encodeCall(IDependencySet.isInDependencySet, (_destination)), + abi.encode(false) + ); + + // Expect a revert with the InvalidChainId selector + vm.expectRevert(InvalidChainId.selector); + + // Call `sendMessage` with a destination that is not in the dependency set to provoke revert + l2ToL2CrossDomainMessenger.sendMessage(_destination, _target, _message); + } + /// @dev Tests that the `relayMessage` function succeeds and emits the correct RelayedMessage event. function testFuzz_relayMessage_succeeds( uint256 _source, @@ -227,7 +266,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -271,7 +310,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -334,7 +373,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -415,7 +454,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -497,7 +536,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -536,7 +575,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -578,7 +617,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -628,7 +667,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); @@ -678,7 +717,7 @@ contract L2ToL2CrossDomainMessengerTest is Test { // Ensure the CrossL2Inbox validates this message vm.mockCall({ callee: Predeploys.CROSS_L2_INBOX, - data: abi.encodeCall(CrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), + data: abi.encodeCall(ICrossL2Inbox.validateMessage, (id, keccak256(sentMessage))), returnData: "" }); diff --git a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol index 87f723a9345..2afb4ba03d1 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainERC20.t.sol @@ -62,7 +62,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainERC20)); - emit IERC7802.CrosschainMint(_to, _amount); + emit IERC7802.CrosschainMint(_to, _amount, SUPERCHAIN_TOKEN_BRIDGE); // Call the `mint` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); @@ -105,7 +105,7 @@ contract SuperchainERC20Test is Test { // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainERC20)); - emit IERC7802.CrosschainBurn(_from, _amount); + emit IERC7802.CrosschainBurn(_from, _amount, SUPERCHAIN_TOKEN_BRIDGE); // Call the `burn` function with the bridge caller vm.prank(SUPERCHAIN_TOKEN_BRIDGE); diff --git a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol index e342a53b602..621acaa3aef 100644 --- a/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol +++ b/packages/contracts-bedrock/test/L2/SuperchainWETH.t.sol @@ -6,7 +6,7 @@ import { CommonTest } from "test/setup/CommonTest.sol"; // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; -import { NotCustomGasToken } from "src/libraries/errors/CommonErrors.sol"; +import { NotCustomGasToken, Unauthorized, ZeroAddress } from "src/libraries/errors/CommonErrors.sol"; import { Preinstalls } from "src/libraries/Preinstalls.sol"; // Interfaces @@ -14,6 +14,7 @@ import { IETHLiquidity } from "src/L2/interfaces/IETHLiquidity.sol"; import { ISuperchainWETH } from "src/L2/interfaces/ISuperchainWETH.sol"; import { IERC7802, IERC165 } from "src/L2/interfaces/IERC7802.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IL2ToL2CrossDomainMessenger } from "src/L2/interfaces/IL2ToL2CrossDomainMessenger.sol"; /// @title SuperchainWETH_Test /// @notice Contract for testing the SuperchainWETH contract. @@ -28,10 +29,14 @@ contract SuperchainWETH_Test is CommonTest { event Withdrawal(address indexed src, uint256 wad); /// @notice Emitted when a crosschain transfer mints tokens. - event CrosschainMint(address indexed to, uint256 amount); + event CrosschainMint(address indexed to, uint256 amount, address indexed sender); /// @notice Emitted when a crosschain transfer burns tokens. - event CrosschainBurn(address indexed from, uint256 amount); + event CrosschainBurn(address indexed from, uint256 amount, address indexed sender); + + event SendETH(address indexed from, address indexed to, uint256 amount, uint256 destination); + + event RelayETH(address indexed from, address indexed to, uint256 amount, uint256 source); address internal constant ZERO_ADDRESS = address(0); @@ -162,7 +167,7 @@ contract SuperchainWETH_Test is CommonTest { // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainWeth)); - emit CrosschainMint(_to, _amount); + emit CrosschainMint(_to, _amount, Predeploys.SUPERCHAIN_TOKEN_BRIDGE); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); @@ -195,7 +200,7 @@ contract SuperchainWETH_Test is CommonTest { // Look for the emit of the `CrosschainMint` event vm.expectEmit(address(superchainWeth)); - emit CrosschainMint(_to, _amount); + emit CrosschainMint(_to, _amount, Predeploys.SUPERCHAIN_TOKEN_BRIDGE); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(true)); @@ -248,7 +253,7 @@ contract SuperchainWETH_Test is CommonTest { // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainWeth)); - emit CrosschainBurn(_from, _amount); + emit CrosschainBurn(_from, _amount, Predeploys.SUPERCHAIN_TOKEN_BRIDGE); // Mock the `isCustomGasToken` function to return false _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); @@ -290,7 +295,7 @@ contract SuperchainWETH_Test is CommonTest { // Look for the emit of the `CrosschainBurn` event vm.expectEmit(address(superchainWeth)); - emit CrosschainBurn(_from, _amount); + emit CrosschainBurn(_from, _amount, Predeploys.SUPERCHAIN_TOKEN_BRIDGE); // Expect to not call the `burn` function in the `ETHLiquidity` contract vm.expectCall(Predeploys.ETH_LIQUIDITY, abi.encodeCall(IETHLiquidity.burn, ()), 0); @@ -324,8 +329,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the internal mint function reverts to protect against accidentally changing the visibility. function testFuzz_calling_internalMintFunction_reverts(address _caller, address _to, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("_mint(address,uint256)", _to, _amount); // nosemgrep: - // sol-style-use-abi-encodecall + // nosemgrep: sol-style-use-abi-encodecall + bytes memory _calldata = abi.encodeWithSignature("_mint(address,uint256)", _to, _amount); vm.expectRevert(bytes("")); // Act @@ -339,8 +344,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the mint function reverts to protect against accidentally changing the visibility. function testFuzz_calling_mintFunction_reverts(address _caller, address _to, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("mint(address,uint256)", _to, _amount); // nosemgrep: - // sol-style-use-abi-encodecall + // nosemgrep: sol-style-use-abi-encodecall + bytes memory _calldata = abi.encodeWithSignature("mint(address,uint256)", _to, _amount); vm.expectRevert(bytes("")); // Act @@ -354,8 +359,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the internal burn function reverts to protect against accidentally changing the visibility. function testFuzz_calling_internalBurnFunction_reverts(address _caller, address _from, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("_burn(address,uint256)", _from, _amount); // nosemgrep: - // sol-style-use-abi-encodecall + // nosemgrep: sol-style-use-abi-encodecall + bytes memory _calldata = abi.encodeWithSignature("_burn(address,uint256)", _from, _amount); vm.expectRevert(bytes("")); // Act @@ -369,8 +374,8 @@ contract SuperchainWETH_Test is CommonTest { /// @notice Test that the burn function reverts to protect against accidentally changing the visibility. function testFuzz_calling_burnFuunction_reverts(address _caller, address _from, uint256 _amount) public { // Arrange - bytes memory _calldata = abi.encodeWithSignature("burn(address,uint256)", _from, _amount); // nosemgrep: - // sol-style-use-abi-encodecall + // nosemgrep: sol-style-use-abi-encodecall + bytes memory _calldata = abi.encodeWithSignature("burn(address,uint256)", _from, _amount); vm.expectRevert(bytes("")); // Act @@ -475,4 +480,207 @@ contract SuperchainWETH_Test is CommonTest { vm.assume(_interfaceId != type(IERC20).interfaceId); assertFalse(superchainWeth.supportsInterface(_interfaceId)); } + + /// @notice Tests the `sendETH` function reverts when the address `_to` is zero. + function testFuzz_sendETH_zeroAddressTo_reverts(address _sender, uint256 _amount, uint256 _chainId) public { + // Expect the revert with `ZeroAddress` selector + vm.expectRevert(ZeroAddress.selector); + + vm.deal(_sender, _amount); + vm.prank(_sender); + // Call the `sendETH` function with the zero address as `_to` + superchainWeth.sendETH{ value: _amount }(ZERO_ADDRESS, _chainId); + } + + /// @notice Tests the `sendETH` function burns the sender ETH, sends the message, and emits the `SendETH` + /// event. + function testFuzz_sendETH_fromNonCustomGasTokenChain_succeeds( + address _sender, + address _to, + uint256 _amount, + uint256 _chainId, + bytes32 _msgHash + ) + external + { + // Assume + vm.assume(_sender != address(ethLiquidity)); + vm.assume(_sender != ZERO_ADDRESS); + vm.assume(_to != ZERO_ADDRESS); + _amount = bound(_amount, 0, type(uint248).max - 1); + + // Arrange + vm.deal(_sender, _amount); + _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); + + // Get the total balance of `_sender` before the send to compare later on the assertions + uint256 _senderBalanceBefore = _sender.balance; + + // Look for the emit of the `SendETH` event + vm.expectEmit(address(superchainWeth)); + emit SendETH(_sender, _to, _amount, _chainId); + + // Expect the call to the `burn` function in the `ETHLiquidity` contract + vm.expectCall(Predeploys.ETH_LIQUIDITY, abi.encodeCall(IETHLiquidity.burn, ()), 1); + + // Mock the call over the `sendMessage` function and expect it to be called properly + bytes memory _message = abi.encodeCall(superchainWeth.relayETH, (_sender, _to, _amount)); + _mockAndExpect( + Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, + abi.encodeCall(IL2ToL2CrossDomainMessenger.sendMessage, (_chainId, address(superchainWeth), _message)), + abi.encode(_msgHash) + ); + + // Call the `sendETH` function + vm.prank(_sender); + bytes32 _returnedMsgHash = superchainWeth.sendETH{ value: _amount }(_to, _chainId); + + // Check the message hash was generated correctly + assertEq(_msgHash, _returnedMsgHash); + + // Check the total supply and balance of `_sender` after the send were updated correctly + assertEq(_sender.balance, _senderBalanceBefore - _amount); + } + + /// @notice Tests the `sendETH` function reverts when called on a custom gas token chain. + function testFuzz_sendETH_fromCustomGasTokenChain_fails( + address _sender, + address _to, + uint256 _amount, + uint256 _chainId + ) + external + { + // Assume + vm.assume(_sender != ZERO_ADDRESS); + vm.assume(_to != ZERO_ADDRESS); + _amount = bound(_amount, 0, type(uint248).max - 1); + + // Arrange + vm.deal(_sender, _amount); + _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(true)); + + // Call the `sendETH` function + vm.prank(_sender); + vm.expectRevert(NotCustomGasToken.selector); + superchainWeth.sendETH{ value: _amount }(_to, _chainId); + } + + /// @notice Tests the `relayETH` function reverts when the caller is not the L2ToL2CrossDomainMessenger. + function testFuzz_relayETH_notMessenger_reverts(address _caller, address _to, uint256 _amount) public { + // Ensure the caller is not the messenger + vm.assume(_caller != Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + + // Expect the revert with `Unauthorized` selector + vm.expectRevert(Unauthorized.selector); + + // Call the `relayETH` function with the non-messenger caller + vm.prank(_caller); + superchainWeth.relayETH(_caller, _to, _amount); + } + + /// @notice Tests the `relayETH` function reverts when the `crossDomainMessageSender` that sent the message is not + /// the same SuperchainWETH. + function testFuzz_relayETH_notCrossDomainSender_reverts( + address _crossDomainMessageSender, + uint256 _source, + address _to, + uint256 _amount + ) + public + { + vm.assume(_crossDomainMessageSender != address(superchainWeth)); + + // Mock the call over the `crossDomainMessageContext` function setting a wrong sender + vm.mockCall( + Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, + abi.encodeCall(IL2ToL2CrossDomainMessenger.crossDomainMessageContext, ()), + abi.encode(_crossDomainMessageSender, _source) + ); + + // Expect the revert with `InvalidCrossDomainSender` selector + vm.expectRevert(ISuperchainWETH.InvalidCrossDomainSender.selector); + + // Call the `relayETH` function with the sender caller + vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + superchainWeth.relayETH(_crossDomainMessageSender, _to, _amount); + } + + /// @notice Tests the `relayETH` function succeeds and sends SuperchainWETH to the recipient on a custom gas token + /// chain. + function testFuzz_relayETH_fromCustomGasTokenChain_succeeds( + address _from, + address _to, + uint256 _amount, + uint256 _source + ) + public + { + // Assume + vm.assume(_to != ZERO_ADDRESS); + _amount = bound(_amount, 0, type(uint248).max - 1); + + // Get the balance of `_to` before the mint to compare later on the assertions + uint256 _toBalanceBefore = superchainWeth.balanceOf(_to); + + // Look for the emit of the `Transfer` event + vm.expectEmit(address(superchainWeth)); + emit Transfer(ZERO_ADDRESS, _to, _amount); + + // Look for the emit of the `RelayETH` event + vm.expectEmit(address(superchainWeth)); + emit RelayETH(_from, _to, _amount, _source); + + // Arrange + _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(true)); + _mockAndExpect( + Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, + abi.encodeCall(IL2ToL2CrossDomainMessenger.crossDomainMessageContext, ()), + abi.encode(address(superchainWeth), _source) + ); + // Expect to not call the `mint` function in the `ETHLiquidity` contract + vm.expectCall(Predeploys.ETH_LIQUIDITY, abi.encodeCall(IETHLiquidity.mint, (_amount)), 0); + + // Act + vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + superchainWeth.relayETH(_from, _to, _amount); + + // Check the total supply and balance of `_to` after the mint were updated correctly + assertEq(superchainWeth.balanceOf(_to), _toBalanceBefore + _amount); + assertEq(superchainWeth.totalSupply(), 0); + assertEq(address(superchainWeth).balance, 0); + } + + /// @notice Tests the `relayETH` function relays the proper amount of ETH and emits the `RelayETH` event. + function testFuzz_relayETH_succeeds(address _from, address _to, uint256 _amount, uint256 _source) public { + // Assume + vm.assume(_to != ZERO_ADDRESS); + assumePayable(_to); + _amount = bound(_amount, 0, type(uint248).max - 1); + + // Arrange + vm.deal(address(superchainWeth), _amount); + vm.deal(Predeploys.ETH_LIQUIDITY, _amount); + _mockAndExpect(address(l1Block), abi.encodeCall(l1Block.isCustomGasToken, ()), abi.encode(false)); + _mockAndExpect( + Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER, + abi.encodeCall(IL2ToL2CrossDomainMessenger.crossDomainMessageContext, ()), + abi.encode(address(superchainWeth), _source) + ); + + uint256 _toBalanceBefore = _to.balance; + + // Look for the emit of the `RelayETH` event + vm.expectEmit(address(superchainWeth)); + emit RelayETH(_from, _to, _amount, _source); + + // Expect the call to the `mint` function in the `ETHLiquidity` contract + vm.expectCall(Predeploys.ETH_LIQUIDITY, abi.encodeCall(IETHLiquidity.mint, (_amount)), 1); + + // Call the `RelayETH` function with the messenger caller + vm.prank(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + superchainWeth.relayETH(_from, _to, _amount); + + assertEq(_to.balance, _toBalanceBefore + _amount); + } } diff --git a/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol b/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol index 3f140e9a072..6d0abaea8d2 100644 --- a/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol +++ b/packages/contracts-bedrock/test/legacy/DeployerWhitelist.t.sol @@ -10,6 +10,11 @@ import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; contract DeployerWhitelist_Test is Test { IDeployerWhitelist list; + address owner = address(12345); + + event OwnerChanged(address oldOwner, address newOwner); + event WhitelistDisabled(address oldOwner); + event WhitelistStatusChanged(address deployer, bool whitelisted); /// @dev Sets up the test suite. function setUp() public { @@ -27,10 +32,102 @@ contract DeployerWhitelist_Test is Test { } /// @dev Tests that `setOwner` correctly sets the contract owner. - function test_storageSlots_succeeds() external { - vm.prank(list.owner()); - list.setOwner(address(1)); + function test_setOwner_succeeds(address _owner) external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + _owner = address(uint160(bound(uint160(_owner), 1, type(uint160).max))); + + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit OwnerChanged(owner, _owner); + list.setOwner(_owner); + + assertEq(list.owner(), _owner); + } + + /// @dev Tests that `setOwner` reverts when the caller is not the owner. + function test_setOwner_callerNotOwner_reverts(address _caller, address _owner) external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.assume(_caller != owner); + + vm.prank(_caller); + vm.expectRevert(bytes("DeployerWhitelist: function can only be called by the owner of this contract")); + list.setOwner(_owner); + } + + /// @dev Tests that `setOwner` reverts when the new owner is the zero address. + function test_setOwner_zeroAddress_reverts() external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.prank(owner); + vm.expectRevert(bytes("DeployerWhitelist: can only be disabled via enableArbitraryContractDeployment")); + list.setOwner(address(0)); + } + + /// @dev Tests that `enableArbitraryContractDeployment` correctly disables the whitelist. + function test_enableArbitraryContractDeployment_succeeds() external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit WhitelistDisabled(owner); + list.enableArbitraryContractDeployment(); + + assertEq(list.owner(), address(0)); + + // Any address is allowed to deploy contracts even if they are not whitelisted + assertEq(list.whitelist(address(1)), false); + assertEq(list.isDeployerAllowed(address(1)), true); + } + + /// @dev Tests that `enableArbitraryContractDeployment` reverts when the caller is not the owner. + function test_enableArbitraryContractDeployment_callerNotOwner_reverts(address _caller) external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.assume(_caller != owner); + + vm.prank(_caller); + vm.expectRevert(bytes("DeployerWhitelist: function can only be called by the owner of this contract")); + list.enableArbitraryContractDeployment(); + } + + /// @dev Tests that `setWhitelistedDeployer` correctly sets the whitelist status of a deployer. + function test_setWhitelistedDeployer_succeeds(address _deployer, bool _isWhitelisted) external { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.prank(owner); + vm.expectEmit(true, true, true, true); + emit WhitelistStatusChanged(_deployer, _isWhitelisted); + list.setWhitelistedDeployer(_deployer, _isWhitelisted); + + assertEq(list.whitelist(_deployer), _isWhitelisted); + + // _deployer is whitelisted or not (and arbitrary contract deployment is not enabled) + assertNotEq(list.owner(), address(0)); + assertEq(list.isDeployerAllowed(_deployer), _isWhitelisted); + } + + /// @dev Tests that `setWhitelistedDeployer` reverts when the caller is not the owner. + function test_setWhitelistedDeployer_callerNotOwner_reverts( + address _caller, + address _deployer, + bool _isWhitelisted + ) + external + { + vm.store(address(list), bytes32(uint256(0)), bytes32(uint256(uint160(owner)))); + assertEq(list.owner(), owner); + + vm.assume(_caller != owner); - assertEq(bytes32(uint256(1)), vm.load(address(list), bytes32(uint256(0)))); + vm.prank(_caller); + vm.expectRevert(bytes("DeployerWhitelist: function can only be called by the owner of this contract")); + list.setWhitelistedDeployer(_deployer, _isWhitelisted); } } diff --git a/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol b/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol index 49758739d2d..203605f3b1b 100644 --- a/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol +++ b/packages/contracts-bedrock/test/legacy/L1BlockNumber.t.sol @@ -4,16 +4,16 @@ pragma solidity 0.8.15; // Testing import { Test } from "forge-std/Test.sol"; -// Contracts -import { IL1BlockNumber } from "src/legacy/interfaces/IL1BlockNumber.sol"; -import { L1Block } from "src/L2/L1Block.sol"; - // Libraries import { Predeploys } from "src/libraries/Predeploys.sol"; import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; +// Interfaces +import { IL1BlockNumber } from "src/legacy/interfaces/IL1BlockNumber.sol"; +import { IL1Block } from "src/L2/interfaces/IL1Block.sol"; + contract L1BlockNumberTest is Test { - L1Block lb; + IL1Block lb; IL1BlockNumber bn; uint64 constant number = 99; @@ -21,7 +21,7 @@ contract L1BlockNumberTest is Test { /// @dev Sets up the test suite. function setUp() external { vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, vm.getDeployedCode("L1Block.sol:L1Block")); - lb = L1Block(Predeploys.L1_BLOCK_ATTRIBUTES); + lb = IL1Block(Predeploys.L1_BLOCK_ATTRIBUTES); bn = IL1BlockNumber( DeployUtils.create1({ _name: "L1BlockNumber", @@ -49,7 +49,7 @@ contract L1BlockNumberTest is Test { /// @dev Tests that `fallback` is correctly dispatched. function test_fallback_succeeds() external { - (bool success, bytes memory ret) = address(bn).call(hex""); + (bool success, bytes memory ret) = address(bn).call(hex"11"); assertEq(success, true); assertEq(ret, abi.encode(number)); } diff --git a/packages/contracts-bedrock/test/legacy/L1ChugSplashProxy.t.sol b/packages/contracts-bedrock/test/legacy/L1ChugSplashProxy.t.sol new file mode 100644 index 00000000000..dcfab12cb60 --- /dev/null +++ b/packages/contracts-bedrock/test/legacy/L1ChugSplashProxy.t.sol @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.15; + +// Testing utilities +import { Test } from "forge-std/Test.sol"; +import { VmSafe } from "forge-std/Vm.sol"; + +// Target contract +import { IL1ChugSplashProxy } from "src/legacy/interfaces/IL1ChugSplashProxy.sol"; +import { DeployUtils } from "scripts/libraries/DeployUtils.sol"; + +contract Owner { + bool public isUpgrading; + + function setIsUpgrading(bool _isUpgrading) public { + isUpgrading = _isUpgrading; + } +} + +contract Implementation { + function setCode(bytes memory) public pure returns (uint256) { + return 1; + } + + function setStorage(bytes32, bytes32) public pure returns (uint256) { + return 2; + } + + function setOwner(address) public pure returns (uint256) { + return 3; + } + + function getOwner() public pure returns (uint256) { + return 4; + } + + function getImplementation() public pure returns (uint256) { + return 5; + } +} + +contract L1ChugSplashProxy_Test is Test { + IL1ChugSplashProxy proxy; + address impl; + address owner = makeAddr("owner"); + address alice = makeAddr("alice"); + + function setUp() public { + proxy = IL1ChugSplashProxy( + DeployUtils.create1({ + _name: "L1ChugSplashProxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ChugSplashProxy.__constructor__, (owner))) + }) + ); + vm.prank(owner); + assertEq(proxy.getOwner(), owner); + + vm.prank(owner); + proxy.setCode(type(Implementation).runtimeCode); + + vm.prank(owner); + impl = proxy.getImplementation(); + } + + /// @notice Tests that the owner can deploy a new implementation with a given runtime code + function test_setCode_whenOwner_succeeds() public { + vm.prank(owner); + proxy.setCode(hex"604260005260206000f3"); + + vm.prank(owner); + assertNotEq(proxy.getImplementation(), impl); + } + + /// @notice Tests that when not the owner, `setCode` delegatecalls the implementation + function test_setCode_whenNotOwner_works() public view { + uint256 ret = Implementation(address(proxy)).setCode(hex"604260005260206000f3"); + assertEq(ret, 1); + } + + /// @notice Tests that when the owner deploys the same bytecode as the existing implementation, + /// it does not deploy a new implementation + function test_setCode_whenOwnerSameBytecode_works() public { + vm.prank(owner); + proxy.setCode(type(Implementation).runtimeCode); + + // does not deploy new implementation + vm.prank(owner); + assertEq(proxy.getImplementation(), impl); + } + + /// @notice Tests that when the owner calls `setCode` with insufficient gas to complete the implementation + /// contract's deployment, it reverts. + /// @dev If this solc version/settings change and modifying this proves time consuming, we can just remove it. + function test_setCode_whenOwnerAndDeployOutOfGas_reverts() public { + // The values below are best gotten by removing the gas limit parameter from the call and running the test with + // a + // verbosity of `-vvvv` then setting the value to a few thousand gas lower than the gas used by the call. + // A faster way to do this for forge coverage cases, is to comment out the optimizer and optimizer runs in + // the foundry.toml file and then run forge test. This is faster because forge test only compiles modified + // contracts unlike forge coverage. + uint256 gasLimit; + + // Because forge coverage always runs with the optimizer disabled, + // if forge coverage is run before testing this with forge test or forge snapshot, forge clean should be + // run first so that it recompiles the contracts using the foundry.toml optimizer settings. + if (vm.isContext(VmSafe.ForgeContext.Coverage)) { + gasLimit = 95_000; + } else if (vm.isContext(VmSafe.ForgeContext.Test) || vm.isContext(VmSafe.ForgeContext.Snapshot)) { + gasLimit = 65_000; + } else { + revert("SafeCall_Test: unknown context"); + } + + vm.prank(owner); + vm.expectRevert(bytes("L1ChugSplashProxy: code was not correctly deployed")); // Ran out of gas + proxy.setCode{ gas: gasLimit }( + hex"fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe" + ); + } + + /// @notice Tests that when the caller is not the owner and the implementation is not set, all calls reverts + function test_calls_whenNotOwnerNoImplementation_reverts() public { + proxy = IL1ChugSplashProxy( + DeployUtils.create1({ + _name: "L1ChugSplashProxy", + _args: DeployUtils.encodeConstructor(abi.encodeCall(IL1ChugSplashProxy.__constructor__, (owner))) + }) + ); + + vm.expectRevert(bytes("L1ChugSplashProxy: implementation is not set yet")); + Implementation(address(proxy)).setCode(hex"604260005260206000f3"); + } + + /// @notice Tests that when the caller is not the owner but the owner has marked `isUpgrading` as true, the call + /// reverts + function test_calls_whenUpgrading_reverts() public { + Owner ownerContract = new Owner(); + vm.prank(owner); + proxy.setOwner(address(ownerContract)); + + ownerContract.setIsUpgrading(true); + + vm.expectRevert(bytes("L1ChugSplashProxy: system is currently being upgraded")); + Implementation(address(proxy)).setCode(hex"604260005260206000f3"); + } + + /// @notice Tests that the owner can set storage of the proxy + function test_setStorage_whenOwner_works() public { + vm.prank(owner); + proxy.setStorage(bytes32(0), bytes32(uint256(42))); + assertEq(vm.load(address(proxy), bytes32(0)), bytes32(uint256(42))); + } + + /// @notice Tests that when not the owner, `setStorage` delegatecalls the implementation + function test_setStorage_whenNotOwner_works() public view { + uint256 ret = Implementation(address(proxy)).setStorage(bytes32(0), bytes32(uint256(42))); + assertEq(ret, 2); + assertEq(vm.load(address(proxy), bytes32(0)), bytes32(uint256(0))); + } + + /// @notice Tests that the owner can set the owner of the proxy + function test_setOwner_whenOwner_works() public { + vm.prank(owner); + proxy.setOwner(alice); + + vm.prank(alice); + assertEq(proxy.getOwner(), alice); + } + + /// @notice Tests that when not the owner, `setOwner` delegatecalls the implementation + function test_setOwner_whenNotOwner_works() public { + uint256 ret = Implementation(address(proxy)).setOwner(alice); + assertEq(ret, 3); + + vm.prank(owner); + assertEq(proxy.getOwner(), owner); + } + + /// @notice Tests that the owner can get the owner of the proxy + function test_getOwner_whenOwner_works() public { + vm.prank(owner); + assertEq(proxy.getOwner(), owner); + } + + /// @notice Tests that when not the owner, `getOwner` delegatecalls the implementation + function test_getOwner_whenNotOwner_works() public view { + uint256 ret = Implementation(address(proxy)).getOwner(); + assertEq(ret, 4); + } + + /// @notice Tests that the owner can get the implementation of the proxy + function test_getImplementation_whenOwner_works() public { + vm.prank(owner); + assertEq(proxy.getImplementation(), impl); + } + + /// @notice Tests that when not the owner, `getImplementation` delegatecalls the implementation + function test_getImplementation_whenNotOwner_works() public view { + uint256 ret = Implementation(address(proxy)).getImplementation(); + assertEq(ret, 5); + } +} diff --git a/packages/contracts-bedrock/test/legacy/LegacyMintableERC20.t.sol b/packages/contracts-bedrock/test/legacy/LegacyMintableERC20.t.sol new file mode 100644 index 00000000000..061b6eee6c5 --- /dev/null +++ b/packages/contracts-bedrock/test/legacy/LegacyMintableERC20.t.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +// Testing utilities +import { CommonTest } from "test/setup/CommonTest.sol"; + +import { LegacyMintableERC20 } from "src/legacy/LegacyMintableERC20.sol"; +import { ILegacyMintableERC20 } from "src/universal/interfaces/ILegacyMintableERC20.sol"; + +contract LegacyMintableERC20_Test is CommonTest { + LegacyMintableERC20 legacyMintableERC20; + + function setUp() public override { + super.setUp(); + + legacyMintableERC20 = new LegacyMintableERC20(address(l2StandardBridge), address(L1Token), "_L2Token_", "_L2T_"); + } + + /// @notice Tests that the constructor sets the correct values + function test_constructor_works() public view { + assertEq(legacyMintableERC20.l2Bridge(), address(l2StandardBridge)); + assertEq(legacyMintableERC20.l1Token(), address(L1Token)); + assertEq(legacyMintableERC20.name(), "_L2Token_"); + assertEq(legacyMintableERC20.symbol(), "_L2T_"); + assertEq(legacyMintableERC20.decimals(), 18); + } + + /// @notice Tests that the contract supports the correct interfaces + function test_supportsInterface_works() public view { + assertEq(legacyMintableERC20.supportsInterface(bytes4(keccak256("supportsInterface(bytes4)"))), true); + assertEq( + legacyMintableERC20.supportsInterface( + ILegacyMintableERC20.l1Token.selector ^ ILegacyMintableERC20.mint.selector + ^ ILegacyMintableERC20.burn.selector + ), + true + ); + } + + /// @notice Tests that the mint function works when called by the bridge + function test_mint_byBridge_succeeds() public { + vm.prank(address(l2StandardBridge)); + legacyMintableERC20.mint(address(this), 1000); + assertEq(legacyMintableERC20.balanceOf(address(this)), 1000); + } + + /// @notice Tests that the mint function fails when called by an address other than the bridge + function test_mint_byNonBridge_reverts() public { + vm.expectRevert(bytes("Only L2 Bridge can mint and burn")); + legacyMintableERC20.mint(address(this), 1000); + } + + /// @notice Tests that the burn function works when called by the bridge + function test_burn_byBridge_succeeds() public { + vm.prank(address(l2StandardBridge)); + legacyMintableERC20.mint(address(this), 1000); + + vm.prank(address(l2StandardBridge)); + legacyMintableERC20.burn(address(this), 1000); + assertEq(legacyMintableERC20.balanceOf(address(this)), 0); + } + + /// @notice Tests that the burn function fails when called by an address other than the bridge + function test_burn_byNonBridge_reverts() public { + vm.expectRevert(bytes("Only L2 Bridge can mint and burn")); + legacyMintableERC20.burn(address(this), 1000); + } +} diff --git a/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol b/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol index 781ada34333..e6d04d971db 100644 --- a/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol +++ b/packages/contracts-bedrock/test/libraries/trie/MerkleTrie.t.sol @@ -3,6 +3,7 @@ pragma solidity 0.8.15; import { Test } from "forge-std/Test.sol"; import { MerkleTrie } from "src/libraries/trie/MerkleTrie.sol"; +import { RLPReader } from "src/libraries/rlp/RLPReader.sol"; import { FFIInterface } from "test/setup/FFIInterface.sol"; import "src/libraries/rlp/RLPErrors.sol"; @@ -372,4 +373,143 @@ contract MerkleTrie_get_Test is Test { vm.expectRevert("MerkleTrie: ran out of proof elements"); MerkleTrie.get(key, proof, root); } + + /// @notice Tests that `get` reverts if a proof node has an unknown prefix + function test_get_unknownNodePrefix_reverts(uint8 prefix) external { + // bound it to only have prefixes where the first nibble is >= 4 + prefix = uint8(bound(prefix, 0x40, 0xff)); + // if the first nibble of the prefix is odd, make it even by adding 16 + if (((prefix / 16) % 2) == 1) { + unchecked { + prefix += 16; + } + // bound it again in case it overflowed + prefix = uint8(bound(prefix, 0x40, 0xff)); + } + + MerkleTrieWrapper wrapper = new MerkleTrieWrapper(); + + bytes memory key = abi.encodePacked( + keccak256(abi.encodePacked(bytes32(0xa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c))) + ); + bytes[] memory proof = new bytes[](5); + proof[0] = + hex"f90211a085ed702d58e6a962ad0e785e5c9036e06d878fd065eb9669122447f6aee7957da05badb8cfd5a7493d928614730af6e14eabe2c93fbac93c853dde3270c446309da01de85a57c524ac56a5bd4bed0b0aa7d963e364ad930ea964d0a42631a77ded4da0fe3143892366faeb9fae1117b888263afe0f74e6c73555fee53a604bf7188431a0af2c79f0dddd15d6f62e3fa60d515c44d58444ad3915c7ca4bddb31c8f148d0ca08f37a2f9093a4aee39519f3a06fe4674cc670fbbbd7a5f4eb096310b7bc1fdc9a086bd12d2031d9714130c687e6822250fa24b3147824780bea96cf8a7406c8966a03e42538ba2da8adaa0eca4550ef62de4dabde8ca06b71ac1b276ff31b67a7655a04a439f7eb6a62c77ec921139925af3359f61d16e083076e0e425583289107d7da0c453a51991b5a4c6174ddff77c0b7d9cc86f05ffda6ff523e2365f19797c7a00a06f43b7b9a118264ab4b6c24d7d3de77f39071a298426bfc27180adfca57d590da0032e0db4dcf122d4bdb1d4ec3c5df5fabd3127bcefe412cb046b7f0d80d11c9fa0560c2b8c9466b8cb5ffd600f24ea0ed9838bfdab7870d505d4387c2468d3c498a0597996e939ff8c29c9e59dc47c111e760450a9c4fe2b065825762da2a1f32495a0e3411c9af104364230c49de5a4d0802c84df18beee9778673364e1747a875622a02a6928825356d8280f361a02285af30e73889f4e55ecb63ed85c8581e07061d680"; + proof[1] = + hex"f90211a0db246208c4cef45e9aeb9f1df1baa8572675bc453f7da538165a2bb9e6a4c416a0d26d82a9ddff901d2a1f9e18506120dec0e5b3c95549c5bff0efc355061ea73fa04f1cedbb5c7df7ee5cc3210980baf5537affb29c661c6a4eeb193bc42e7fbc74a0acea389e0cf577d0e13483d98b15c82618ac18b7cc4a479981e3e672ddd16867a0ef59a06aeea1eb5ba1313bbe1fa74ff264d84d7319ab6178213734b5b5efa9c1a08f85dc6001713d77aa4e12982dfdb28fd1c7dcc290d46f2749e8a7d67ba2a694a0f6698ff794881edc50340b75311de64ce3da5f97debcfdfd4d20de57ef3ba7eba0680071ce05e9c7915f731bac8b9673332d1d77ea1f7dadab36d9b233eea32ba4a035ad3686f436232360c2aa364c9f2aa2081318b9fb33cd1050d69ee46f791d62a03b495b3d65d9ae39680a0f835c1d1378d218f7b1fb88d2b2c6ac6ef916f09172a0a808d1e8c632d9a6cfeb3c2c123a58b5b3e1998d4bd02c5fb0f7c5d4ba1338e6a0369376e9152831135ff3a902c9740cf22951d67edd51bf0541565e379d7efc25a0cc26d7fa1c326bc14950e92d9de78e4ed8372ff9727dec34602f24057b3a9b30a0278081783022e748dc70874a72377038935c00c1f0a24bbb8cd0fc208d8b68f4a06c4e83593571b94d08cb78ece0de4920b02a650a47a16583f94c8fe35f724707a0cd7eb9d730e5138fd943200b577e7bbb827d010a50d19af2f4b19ef19658156d80"; + proof[2] = + hex"f90211a0065f58fbe63e8e3387e91047d5b7a4532e7d9de0abc47f04791aef27a140fdb5a0858beea29778551c01b0d3e542d707675856da9c3f1d065c845e55c24d77be89a0e90a410489eff6f4f8d70b0cce1fb1339117ec0f6f1db195a6cc410509a2ebaea078ba7fe504e8d01d57f6bee52c4938d779108e500b5923272441ed2964b8c45da0f0430ed9fa807e5fb6ace75f8766ea60009d8539e00006e359f5f7bc38a76596a0a98a7938db99a2d80abea6349de42bf2576c9e51cc715c85fbacab365ec16f5ba026fadc7d124a456c62ada928eaede3e80611e3e6f99041f7393f062e9e788c8ca0ca48cad1e00d22d6146173341a6060378e738be727a7265a923cf6bfd1f8b610a0f8a4aae21a78ac28e2a61f50396f9a80f6c8232fe4afa203160361c0962242baa09a1029479959fb29b4da7239393fd6ca20bc376d860324f429a51b0e0565a158a0eefb84d3943d680e176258dffe0104ac48c171a8574a811535256a2d8ba531dea062a3d709a2f70ba1970842c4f20a602291273d1f6022e7a14cde2afdcd51e795a0397e6b9b87012cd79cbd0bb7daa4cc43830a673d80b65fb88c0449140175d89ca0f8a4c73c0078cbd32961227910e3f9315bc587716062e39f66be19747ccf9b67a0ea4bdd1b187fdba273a8625f88f284994d19c38ec58651839852665717d953d9a0319ebf356f45da83c7f106f1fd3decbf15f651fad3389a0d279602cdea8ee11480"; + proof[3] = + hex"f8f1a069a092c7a950214e7e45b99012dc8ad112eab0fc94ae5ca9efbd6949068384f280a0b25c46db67ef7cf0c47bb400c31c85a26c5a204431527c964c8ecaf3d63e52cc80a01911a2a74db0d8d182447176e23f25556d1a1eaa0afad96453f2d64876ad88e480808080a04a0ca9e3bed1bc3e3c819384d19b6d5e523164a6520c4eb42e828a63ef730ae38080a03b598ed1b9269d4b05e2e75cfb54298d25437669870c919a59a147d2d256fdba80a0db2d655057c83107a73d086cfdd8fcc74739bb48c652eb0ce597178ecf96b39aa05c66ac392a761341b9c22b773ea19af311f34ef537640b9bb96842ec6ace913280"; + + proof[4] = bytes.concat( + hex"f69f", + bytes1(prefix), + hex"4dcf44e265ba93879b2da89e1b16ab48fc5eb8e31bc16b0612d6da8463f195942536c09e5f5691498805884fa37811be3b2bddb4" + ); + + bytes32 root; + (proof[0], proof[1], proof[2], proof[3], root) = rehashOtherElements(proof[4]); + + vm.expectRevert("MerkleTrie: received a node with an unknown prefix"); + wrapper.get(key, proof, root); + } + + /// @notice Tests that `get` reverts if a proof node is unparsable i.e list length is not 2 or 17 + function test_get_unparsableNode_reverts(uint8 listLen) external { + listLen = uint8(bound(listLen, 1, RLPReader.MAX_LIST_LENGTH)); + if (listLen == 2 || listLen == 17) { + listLen++; + } + + MerkleTrieWrapper wrapper = new MerkleTrieWrapper(); + + bytes memory key = abi.encodePacked( + keccak256(abi.encodePacked(bytes32(0xa15bc60c955c405d20d9149c709e2460f1c2d9a497496a7f46004d1772c3054c))) + ); + bytes[] memory proof = new bytes[](5); + proof[0] = + hex"f90211a085ed702d58e6a962ad0e785e5c9036e06d878fd065eb9669122447f6aee7957da05badb8cfd5a7493d928614730af6e14eabe2c93fbac93c853dde3270c446309da01de85a57c524ac56a5bd4bed0b0aa7d963e364ad930ea964d0a42631a77ded4da0fe3143892366faeb9fae1117b888263afe0f74e6c73555fee53a604bf7188431a0af2c79f0dddd15d6f62e3fa60d515c44d58444ad3915c7ca4bddb31c8f148d0ca08f37a2f9093a4aee39519f3a06fe4674cc670fbbbd7a5f4eb096310b7bc1fdc9a086bd12d2031d9714130c687e6822250fa24b3147824780bea96cf8a7406c8966a03e42538ba2da8adaa0eca4550ef62de4dabde8ca06b71ac1b276ff31b67a7655a04a439f7eb6a62c77ec921139925af3359f61d16e083076e0e425583289107d7da0c453a51991b5a4c6174ddff77c0b7d9cc86f05ffda6ff523e2365f19797c7a00a06f43b7b9a118264ab4b6c24d7d3de77f39071a298426bfc27180adfca57d590da0032e0db4dcf122d4bdb1d4ec3c5df5fabd3127bcefe412cb046b7f0d80d11c9fa0560c2b8c9466b8cb5ffd600f24ea0ed9838bfdab7870d505d4387c2468d3c498a0597996e939ff8c29c9e59dc47c111e760450a9c4fe2b065825762da2a1f32495a0e3411c9af104364230c49de5a4d0802c84df18beee9778673364e1747a875622a02a6928825356d8280f361a02285af30e73889f4e55ecb63ed85c8581e07061d680"; + proof[1] = + hex"f90211a0db246208c4cef45e9aeb9f1df1baa8572675bc453f7da538165a2bb9e6a4c416a0d26d82a9ddff901d2a1f9e18506120dec0e5b3c95549c5bff0efc355061ea73fa04f1cedbb5c7df7ee5cc3210980baf5537affb29c661c6a4eeb193bc42e7fbc74a0acea389e0cf577d0e13483d98b15c82618ac18b7cc4a479981e3e672ddd16867a0ef59a06aeea1eb5ba1313bbe1fa74ff264d84d7319ab6178213734b5b5efa9c1a08f85dc6001713d77aa4e12982dfdb28fd1c7dcc290d46f2749e8a7d67ba2a694a0f6698ff794881edc50340b75311de64ce3da5f97debcfdfd4d20de57ef3ba7eba0680071ce05e9c7915f731bac8b9673332d1d77ea1f7dadab36d9b233eea32ba4a035ad3686f436232360c2aa364c9f2aa2081318b9fb33cd1050d69ee46f791d62a03b495b3d65d9ae39680a0f835c1d1378d218f7b1fb88d2b2c6ac6ef916f09172a0a808d1e8c632d9a6cfeb3c2c123a58b5b3e1998d4bd02c5fb0f7c5d4ba1338e6a0369376e9152831135ff3a902c9740cf22951d67edd51bf0541565e379d7efc25a0cc26d7fa1c326bc14950e92d9de78e4ed8372ff9727dec34602f24057b3a9b30a0278081783022e748dc70874a72377038935c00c1f0a24bbb8cd0fc208d8b68f4a06c4e83593571b94d08cb78ece0de4920b02a650a47a16583f94c8fe35f724707a0cd7eb9d730e5138fd943200b577e7bbb827d010a50d19af2f4b19ef19658156d80"; + proof[2] = + hex"f90211a0065f58fbe63e8e3387e91047d5b7a4532e7d9de0abc47f04791aef27a140fdb5a0858beea29778551c01b0d3e542d707675856da9c3f1d065c845e55c24d77be89a0e90a410489eff6f4f8d70b0cce1fb1339117ec0f6f1db195a6cc410509a2ebaea078ba7fe504e8d01d57f6bee52c4938d779108e500b5923272441ed2964b8c45da0f0430ed9fa807e5fb6ace75f8766ea60009d8539e00006e359f5f7bc38a76596a0a98a7938db99a2d80abea6349de42bf2576c9e51cc715c85fbacab365ec16f5ba026fadc7d124a456c62ada928eaede3e80611e3e6f99041f7393f062e9e788c8ca0ca48cad1e00d22d6146173341a6060378e738be727a7265a923cf6bfd1f8b610a0f8a4aae21a78ac28e2a61f50396f9a80f6c8232fe4afa203160361c0962242baa09a1029479959fb29b4da7239393fd6ca20bc376d860324f429a51b0e0565a158a0eefb84d3943d680e176258dffe0104ac48c171a8574a811535256a2d8ba531dea062a3d709a2f70ba1970842c4f20a602291273d1f6022e7a14cde2afdcd51e795a0397e6b9b87012cd79cbd0bb7daa4cc43830a673d80b65fb88c0449140175d89ca0f8a4c73c0078cbd32961227910e3f9315bc587716062e39f66be19747ccf9b67a0ea4bdd1b187fdba273a8625f88f284994d19c38ec58651839852665717d953d9a0319ebf356f45da83c7f106f1fd3decbf15f651fad3389a0d279602cdea8ee11480"; + proof[3] = + hex"f8f1a069a092c7a950214e7e45b99012dc8ad112eab0fc94ae5ca9efbd6949068384f280a0b25c46db67ef7cf0c47bb400c31c85a26c5a204431527c964c8ecaf3d63e52cc80a01911a2a74db0d8d182447176e23f25556d1a1eaa0afad96453f2d64876ad88e480808080a04a0ca9e3bed1bc3e3c819384d19b6d5e523164a6520c4eb42e828a63ef730ae38080a03b598ed1b9269d4b05e2e75cfb54298d25437669870c919a59a147d2d256fdba80a0db2d655057c83107a73d086cfdd8fcc74739bb48c652eb0ce597178ecf96b39aa05c66ac392a761341b9c22b773ea19af311f34ef537640b9bb96842ec6ace913280"; + proof[4] = + hex"f69f204dcf44e265ba93879b2da89e1b16ab48fc5eb8e31bc16b0612d6da8463f195942536c09e5f5691498805884fa37811be3b2bddb4"; // Correct + // leaf node + + bytes32 root = keccak256(proof[0]); + + // Should not revert + wrapper.get(key, proof, root); + + if (listLen > 3) { + // Node with list > 3 + proof[4] = + hex"f8379f204dcf44e265ba93879b2da89e1b16ab48fc5eb8e31bc16b0612d6da8463f195942536c09e5f5691498805884fa37811be3b2bddb480"; + for (uint256 i; i < listLen - 3; i++) { + proof[4] = bytes.concat(proof[4], hex"80"); + } + proof[4][1] = bytes1(uint8(proof[4][1]) + (listLen - 3)); + // rehash all proof elements and insert it into the proof element above it + (proof[0], proof[1], proof[2], proof[3], root) = rehashOtherElements(proof[4]); + + vm.expectRevert("MerkleTrie: received an unparseable node"); + wrapper.get(key, proof, root); + } else if (listLen == 1) { + // Node with list of 1 + proof[4] = hex"e09f204dcf44e265ba93879b2da89e1b16ab48fc5eb8e31bc16b0612d6da8463f1"; + // rehash all proof elements and insert it into the proof element above it + (proof[0], proof[1], proof[2], proof[3], root) = rehashOtherElements(proof[4]); + + vm.expectRevert("MerkleTrie: received an unparseable node"); + wrapper.get(key, proof, root); + } else if (listLen == 3) { + // Node with list of 3 + proof[4] = + hex"f79f204dcf44e265ba93879b2da89e1b16ab48fc5eb8e31bc16b0612d6da8463f195942536c09e5f5691498805884fa37811be3b2bddb480"; + // rehash all proof elements and insert it into the proof element above it + (proof[0], proof[1], proof[2], proof[3], root) = rehashOtherElements(proof[4]); + + vm.expectRevert("MerkleTrie: received an unparseable node"); + wrapper.get(key, proof, root); + } + } + + function rehashOtherElements(bytes memory _proof4) + private + pure + returns (bytes memory proof0_, bytes memory proof1_, bytes memory proof2_, bytes memory proof3_, bytes32 root_) + { + // rehash all proof elements and insert it into the proof element above it + proof3_ = bytes.concat( + hex"f8f1a069a092c7a950214e7e45b99012dc8ad112eab0fc94ae5ca9efbd6949068384f280a0b25c46db67ef7cf0c47bb400c31c85a26c5a204431527c964c8ecaf3d63e52cc80a0", + keccak256(_proof4), + hex"80808080a04a0ca9e3bed1bc3e3c819384d19b6d5e523164a6520c4eb42e828a63ef730ae38080a03b598ed1b9269d4b05e2e75cfb54298d25437669870c919a59a147d2d256fdba80a0db2d655057c83107a73d086cfdd8fcc74739bb48c652eb0ce597178ecf96b39aa05c66ac392a761341b9c22b773ea19af311f34ef537640b9bb96842ec6ace913280" + ); + proof2_ = bytes.concat( + hex"f90211a0065f58fbe63e8e3387e91047d5b7a4532e7d9de0abc47f04791aef27a140fdb5a0858beea29778551c01b0d3e542d707675856da9c3f1d065c845e55c24d77be89a0e90a410489eff6f4f8d70b0cce1fb1339117ec0f6f1db195a6cc410509a2ebaea078ba7fe504e8d01d57f6bee52c4938d779108e500b5923272441ed2964b8c45da0f0430ed9fa807e5fb6ace75f8766ea60009d8539e00006e359f5f7bc38a76596a0a98a7938db99a2d80abea6349de42bf2576c9e51cc715c85fbacab365ec16f5ba0", + keccak256(proof3_), + hex"a0ca48cad1e00d22d6146173341a6060378e738be727a7265a923cf6bfd1f8b610a0f8a4aae21a78ac28e2a61f50396f9a80f6c8232fe4afa203160361c0962242baa09a1029479959fb29b4da7239393fd6ca20bc376d860324f429a51b0e0565a158a0eefb84d3943d680e176258dffe0104ac48c171a8574a811535256a2d8ba531dea062a3d709a2f70ba1970842c4f20a602291273d1f6022e7a14cde2afdcd51e795a0397e6b9b87012cd79cbd0bb7daa4cc43830a673d80b65fb88c0449140175d89ca0f8a4c73c0078cbd32961227910e3f9315bc587716062e39f66be19747ccf9b67a0ea4bdd1b187fdba273a8625f88f284994d19c38ec58651839852665717d953d9a0319ebf356f45da83c7f106f1fd3decbf15f651fad3389a0d279602cdea8ee11480" + ); + proof1_ = bytes.concat( + hex"f90211a0db246208c4cef45e9aeb9f1df1baa8572675bc453f7da538165a2bb9e6a4c416a0d26d82a9ddff901d2a1f9e18506120dec0e5b3c95549c5bff0efc355061ea73fa04f1cedbb5c7df7ee5cc3210980baf5537affb29c661c6a4eeb193bc42e7fbc74a0acea389e0cf577d0e13483d98b15c82618ac18b7cc4a479981e3e672ddd16867a0ef59a06aeea1eb5ba1313bbe1fa74ff264d84d7319ab6178213734b5b5efa9c1a08f85dc6001713d77aa4e12982dfdb28fd1c7dcc290d46f2749e8a7d67ba2a694a0f6698ff794881edc50340b75311de64ce3da5f97debcfdfd4d20de57ef3ba7eba0680071ce05e9c7915f731bac8b9673332d1d77ea1f7dadab36d9b233eea32ba4a035ad3686f436232360c2aa364c9f2aa2081318b9fb33cd1050d69ee46f791d62a03b495b3d65d9ae39680a0f835c1d1378d218f7b1fb88d2b2c6ac6ef916f09172a0a808d1e8c632d9a6cfeb3c2c123a58b5b3e1998d4bd02c5fb0f7c5d4ba1338e6a0369376e9152831135ff3a902c9740cf22951d67edd51bf0541565e379d7efc25a0", + keccak256(proof2_), + hex"a0278081783022e748dc70874a72377038935c00c1f0a24bbb8cd0fc208d8b68f4a06c4e83593571b94d08cb78ece0de4920b02a650a47a16583f94c8fe35f724707a0cd7eb9d730e5138fd943200b577e7bbb827d010a50d19af2f4b19ef19658156d80" + ); + proof0_ = bytes.concat( + hex"f90211a085ed702d58e6a962ad0e785e5c9036e06d878fd065eb9669122447f6aee7957da05badb8cfd5a7493d928614730af6e14eabe2c93fbac93c853dde3270c446309da0", + keccak256(proof1_), + hex"a0fe3143892366faeb9fae1117b888263afe0f74e6c73555fee53a604bf7188431a0af2c79f0dddd15d6f62e3fa60d515c44d58444ad3915c7ca4bddb31c8f148d0ca08f37a2f9093a4aee39519f3a06fe4674cc670fbbbd7a5f4eb096310b7bc1fdc9a086bd12d2031d9714130c687e6822250fa24b3147824780bea96cf8a7406c8966a03e42538ba2da8adaa0eca4550ef62de4dabde8ca06b71ac1b276ff31b67a7655a04a439f7eb6a62c77ec921139925af3359f61d16e083076e0e425583289107d7da0c453a51991b5a4c6174ddff77c0b7d9cc86f05ffda6ff523e2365f19797c7a00a06f43b7b9a118264ab4b6c24d7d3de77f39071a298426bfc27180adfca57d590da0032e0db4dcf122d4bdb1d4ec3c5df5fabd3127bcefe412cb046b7f0d80d11c9fa0560c2b8c9466b8cb5ffd600f24ea0ed9838bfdab7870d505d4387c2468d3c498a0597996e939ff8c29c9e59dc47c111e760450a9c4fe2b065825762da2a1f32495a0e3411c9af104364230c49de5a4d0802c84df18beee9778673364e1747a875622a02a6928825356d8280f361a02285af30e73889f4e55ecb63ed85c8581e07061d680" + ); + root_ = keccak256(proof0_); + } +} + +contract MerkleTrieWrapper { + function get(bytes memory key, bytes[] memory proof, bytes32 root) external pure returns (bytes memory) { + return MerkleTrie.get(key, proof, root); + } } diff --git a/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol b/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol index 0e6755b0e63..c5791e06868 100644 --- a/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol +++ b/packages/contracts-bedrock/test/periphery/drippie/Drippie.t.sol @@ -16,10 +16,6 @@ import { SimpleStorage } from "test/mocks/SimpleStorage.sol"; contract TestDrippie is Drippie { constructor(address owner) Drippie(owner) { } - function dripStatus(string memory name) external view returns (Drippie.DripStatus) { - return drips[name].status; - } - function dripStateLast(string memory name) external view returns (uint256) { return drips[name].last; } @@ -131,12 +127,12 @@ contract Drippie_Test is Test { vm.prank(drippie.owner()); drippie.create(dripName, cfg); - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); Drippie.DripConfig memory config = drippie.dripConfig(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.PAUSED)); - assertEq(config.interval, cfg.interval); + assertEq(drippie.getDripInterval(dripName), cfg.interval); assertEq(config.reentrant, cfg.reentrant); assertEq(address(config.dripcheck), address(cfg.dripcheck)); assertEq(config.checkparams, cfg.checkparams); @@ -186,7 +182,7 @@ contract Drippie_Test is Test { address owner = drippie.owner(); { - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.PAUSED)); } @@ -198,7 +194,7 @@ contract Drippie_Test is Test { drippie.status(dripName, Drippie.DripStatus.ACTIVE); { - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.ACTIVE)); } @@ -210,7 +206,7 @@ contract Drippie_Test is Test { drippie.status(dripName, Drippie.DripStatus.PAUSED); { - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.PAUSED)); } } @@ -252,7 +248,7 @@ contract Drippie_Test is Test { drippie.status(dripName, Drippie.DripStatus.ARCHIVED); - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.ARCHIVED)); } @@ -463,7 +459,7 @@ contract Drippie_Test is Test { function test_not_active_reverts() external { _createDefaultDrip(dripName); - Drippie.DripStatus status = drippie.dripStatus(dripName); + Drippie.DripStatus status = drippie.getDripStatus(dripName); assertEq(uint256(status), uint256(Drippie.DripStatus.PAUSED)); vm.prank(drippie.owner()); diff --git a/packages/contracts-bedrock/test/periphery/faucet/Faucet.t.sol b/packages/contracts-bedrock/test/periphery/faucet/Faucet.t.sol index a0ef75f28d5..b5f156cf1a8 100644 --- a/packages/contracts-bedrock/test/periphery/faucet/Faucet.t.sol +++ b/packages/contracts-bedrock/test/periphery/faucet/Faucet.t.sol @@ -103,6 +103,24 @@ contract FaucetTest is Faucet_Initializer { assertEq(faucet.ADMIN(), faucetContractAdmin); } + function test_configure_whenAdmin_succeeds() external { + vm.startPrank(faucetContractAdmin); + faucet.configure(optimistNftFam, Faucet.ModuleConfig("OptimistNftModule", true, 1 days, 1 ether)); + + (string memory name, bool enabled, uint256 ttl, uint256 amount) = faucet.modules(optimistNftFam); + assertEq(name, "OptimistNftModule"); + assertEq(enabled, true); + assertEq(ttl, 1 days); + assertEq(amount, 1 ether); + + assertTrue(faucet.isModuleEnabled(optimistNftFam)); + } + + function test_configure_whenNotAdmin_reverts() external { + vm.expectRevert("Faucet: function can only be called by admin"); + faucet.configure(optimistNftFam, Faucet.ModuleConfig("OptimistNftModule", true, 1 days, 1 ether)); + } + function test_authAdmin_drip_succeeds() external { _enableFaucetAuthModules(); bytes32 nonce = faucetHelper.consumeNonce();