Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ contracts/node_modules/
gas-oracle/app/target/
node/build/
ops/docker/.devnet/
ops/docker/.devnet-setup-*/
ops/reth-cross-test/
ops/publicnode/
docs/
17 changes: 11 additions & 6 deletions .github/workflows/deployer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ on:
branches:
- main
paths:
- "ops/l2-genesis/**"
- ".github/workflows/deployer.yaml"
- "ops/**"
- "contracts/**"
- "Makefile"
- ".github/workflows/deployer.yml"
pull_request:
paths:
- "build/**"
- "bindings/**"
- "node/**"
- "ops/l2-genesis/**"
- ".github/workflows/deployer.yaml"
- "ops/**"
- "contracts/**"
- "Makefile"
- ".github/workflows/deployer.yml"

permissions:
contents: read
Expand Down Expand Up @@ -44,5 +48,6 @@ jobs:
uses: actions/setup-go@v5
with:
go-version: 1.24.x
- name: Run tests
run: make test
- name: Verify operational commands, scripts and Go packages
working-directory: .
run: make ops-check
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ build/bin
cache
out
ops/docker/.devnet
ops/docker/.devnet-setup-*
/ops/tools/build/
ops/l2-genesis/.devnet
ops/l2-genesis/.qanet
__pycache__/
*.py[cod]
ops/l2-genesis/.testnet
ops/l2-genesis/.holesky
ops/l2-genesis/.hoodi
Expand All @@ -31,4 +35,4 @@ contracts/mainnet.json
.env

# logs
*.log
*.log
58 changes: 32 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -103,30 +103,30 @@ fmt-go:

################## docker build ####################

docker-build:
cd ops/docker && docker compose build
docker-build: go-ubuntu-builder
cd ops/docker && docker compose --project-name docker $(DEVNET_COMPOSE_ENV) $(DEVNET_COMPOSE_FILES) build
.PHONY: docker-build

go-rust-builder:
@if [ -z "$(shell docker images -q morph/go-rust-builder 2> /dev/null)" ]; then \
@if ! docker image inspect morph/go-rust-builder:go-1.24-rust-nightly-2023-12-03 >/dev/null 2>&1; then \
echo "Docker image morph/go-rust-builder does not exist. Building..."; \
cd ops/docker/intermediate && docker build -t morph/go-rust-builder:go-1.22-rust-nightly-2023-12-03 . -f go-rust-builder.Dockerfile; \
cd ops/docker/intermediate && docker build -t morph/go-rust-builder:go-1.24-rust-nightly-2023-12-03 . -f go-rust-builder.Dockerfile; \
else \
echo "Docker image morph/go-rust-builder already exists."; \
fi
.PHONY: go-rust-builder

go-rust-alpine-builder:
@if [ -z "$(shell docker images -q morph/go-rust-alpine-builder 2> /dev/null)" ]; then \
@if ! docker image inspect morph/go-rust-alpine-builder:go-1.24-rust-nightly-2023-12-03 >/dev/null 2>&1; then \
echo "Docker image morph/go-rust-alpine-builder does not exist. Building..."; \
cd ops/docker/intermediate && docker build -t morph/go-rust-alpine-builder:go-1.22-rust-nightly-2023-12-03 . -f go-rust-alpine-builder.Dockerfile; \
cd ops/docker/intermediate && docker build -t morph/go-rust-alpine-builder:go-1.24-rust-nightly-2023-12-03 . -f go-rust-alpine-builder.Dockerfile; \
else \
echo "Docker image morph/go-rust-alpine-builder already exists."; \
fi
.PHONY: go-rust-alpine-builder

go-ubuntu-builder:
@if [ -z "$(shell docker images -q morph/go-ubuntu-builder 2> /dev/null)" ]; then \
@if ! docker image inspect morph/go-ubuntu-builder:go-1.24-ubuntu >/dev/null 2>&1; then \
echo "Docker image morph/go-ubuntu-builder does not exist. Building..."; \
cd ops/docker/intermediate && docker build -t morph/go-ubuntu-builder:go-1.24-ubuntu . -f go-ubuntu-builder.Dockerfile; \
else \
Expand All @@ -136,7 +136,16 @@ go-ubuntu-builder:

################## devnet 2 nodes ####################

ops-check:
python3 -m unittest discover -s ops/devnet-morph/tests -v
python3 -m unittest discover -s ops/publicnode/tests -v
$(MAKE) -C ops/l2-genesis test
$(MAKE) -C ops/tools test
.PHONY: ops-check

EXECUTION_CLIENT ?= geth
GO_BUILDER_IMAGE ?= morph/go-ubuntu-builder:go-1.24-ubuntu
export GO_BUILDER_IMAGE
DEVNET_CLUSTER ?= false
DEVNET_CLUSTER_ENABLED := $(filter true 1 yes,$(DEVNET_CLUSTER))
DEVNET_SEQUENCER_PRIVATE_KEY ?= 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
Expand All @@ -162,6 +171,8 @@ export MORPH_RETH_DOCKER_TARGET
export MORPH_RETH_ENTRYPOINT
DEVNET_COMPOSE_FILES := -f docker-compose-devnet.yml
DEVNET_CLEAN_COMPOSE_FILES := -f docker-compose-devnet.yml -f docker-compose-cluster.yml -f docker-compose-reth.yml -f docker-compose-cluster-reth.yml
DEVNET_RUNTIME_ENV := $(if $(wildcard ops/l2-genesis/.devnet/runtime.env),$(abspath ops/l2-genesis/.devnet/runtime.env),/dev/null)
DEVNET_COMPOSE_ENV := --env-file "$(DEVNET_RUNTIME_ENV)"

# The cluster topology is layered before the execution-client override, so that
# the reth files have the last word on the ha-el-* image, entrypoint and
Expand Down Expand Up @@ -190,7 +201,7 @@ $(error unsupported EXECUTION_CLIENT "$(EXECUTION_CLIENT)", expected "geth" or "
endif

devnet-up: $(DEVNET_EXECUTION_DEPS) go-ubuntu-builder
python3 ops/devnet-morph/main.py --polyrepo-dir=. --execution-client=$(EXECUTION_CLIENT) \
@python3 ops/devnet-morph/main.py --polyrepo-dir=. --execution-client=$(EXECUTION_CLIENT) \
$(if $(DEVNET_CLUSTER_ENABLED),--cluster,) \
--sequencer-private-key=$(DEVNET_SEQUENCER_PRIVATE_KEY) \
--sequencer-address=$(DEVNET_SEQUENCER_ADDRESS) \
Expand All @@ -210,43 +221,44 @@ devnet-up-cluster-reth:
.PHONY: devnet-up-cluster-reth

devnet-up-debugccc: $(DEVNET_EXECUTION_DEPS) go-ubuntu-builder
python3 ops/devnet-morph/main.py --polyrepo-dir=. --execution-client=$(EXECUTION_CLIENT) --debugccc \
@python3 ops/devnet-morph/main.py --polyrepo-dir=. --execution-client=$(EXECUTION_CLIENT) --debugccc \
$(if $(DEVNET_CLUSTER_ENABLED),--cluster,) \
--sequencer-private-key=$(DEVNET_SEQUENCER_PRIVATE_KEY) \
--sequencer-address=$(DEVNET_SEQUENCER_ADDRESS) \
--sequencer-upgrade-offset-seconds=$(DEVNET_SEQUENCER_UPGRADE_OFFSET_SECONDS)
.PHONY: devnet-up-debugccc

devnet-down:
cd ops/docker && docker compose $(DEVNET_COMPOSE_FILES) down
cd ops/docker && docker compose --project-name docker $(DEVNET_COMPOSE_ENV) $(DEVNET_COMPOSE_FILES) down
.PHONY: devnet-down

devnet-down-reth:
$(MAKE) devnet-down EXECUTION_CLIENT=reth
.PHONY: devnet-down-reth

devnet-clean-build: devnet-l1-clean
cd ops/docker && docker compose $(DEVNET_CLEAN_COMPOSE_FILES) down --volumes --remove-orphans
docker volume ls --filter label=com.docker.compose.project=docker --format='{{.Name}}' | xargs docker volume rm 2>/dev/null || true
cd ops/docker && docker compose --project-name docker $(DEVNET_COMPOSE_ENV) $(DEVNET_CLEAN_COMPOSE_FILES) down --volumes --remove-orphans
rm -rf ops/l2-genesis/.devnet
rm -rf ops/docker/.devnet
rm -rf ops/docker/consensus ops/docker/execution
.PHONY: devnet-clean-build

devnet-clean: devnet-clean-build
docker image ls '*morph*' --format='{{.Repository}}' | xargs -r docker rmi
docker image ls '*sentry-*' --format='{{.Repository}}' | xargs -r docker rmi
@for image in morph-geth:latest morph-node:latest morph-tx-submitter:latest morph-oracle:latest; do \
if docker image inspect "$$image" >/dev/null 2>&1; then docker image rm "$$image" || exit $$?; fi; \
done
.PHONY: devnet-clean

devnet-l1:
python3 ops/devnet-morph/main.py --polyrepo-dir=. --only-l1
.PHONY: devnet-l1

devnet-l1-clean:
@cd ops/docker && ./layer1/scripts/clean.sh
@cd ops/docker && COMPOSE_PROJECT_NAME=docker DEVNET_RUNTIME_ENV="$(DEVNET_RUNTIME_ENV)" ./layer1/scripts/clean.sh
.PHONY: devnet-l1-clean

devnet-logs:
@(cd ops/docker && docker compose $(DEVNET_COMPOSE_FILES) logs -f)
@(cd ops/docker && docker compose --project-name docker $(DEVNET_COMPOSE_ENV) $(DEVNET_COMPOSE_FILES) logs -f)
.PHONY: devnet-logs

reth-image:
Expand All @@ -259,19 +271,13 @@ reth:
.PHONY: reth

# tx-submitter
SUBMITTERS := $(shell grep -o 'tx-submitter-[0-9]*[^:]' ops/docker/docker-compose-devnet.yml | sort | uniq)
rebuild-all-tx-submitter:
@for submitter in $(SUBMITTERS); do \
docker compose -f ./ops/docker/docker-compose-devnet.yml up -d --build $$submitter --no-deps; \
done
@python3 ops/devnet-morph/main.py --polyrepo-dir=. --service-action=rebuild
stop-all-tx-submitter:
@for submitter in $(SUBMITTERS); do \
docker compose -f ./ops/docker/docker-compose-devnet.yml stop $$submitter; \
done
@python3 ops/devnet-morph/main.py --polyrepo-dir=. --service-action=stop
start-all-tx-submitter:
@for submitter in $(SUBMITTERS); do \
docker compose -f ./ops/docker/docker-compose-devnet.yml start $$submitter; \
done
@python3 ops/devnet-morph/main.py --polyrepo-dir=. --service-action=start
.PHONY: rebuild-all-tx-submitter stop-all-tx-submitter start-all-tx-submitter

# build geth
geth: submodules
Expand Down
33 changes: 21 additions & 12 deletions contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,27 @@ yarn echidna:aliasing

### Deployment

#### Configuration

1. Create or modify a file `<network-name>.ts` inside of the [`deploy-config`](./deploy-config/) folder.
2. Fill out this file according to the `deployConfigSpec` located inside of the [`hardhat.config.ts](./hardhat.config.ts)
3. Optionally: Run `npx hardhat generate-deploy-config --network <network-name>` to generate the associated JSON file. This is required if using `op-chain-ops`.

#### Execution

1. Copy `.env.example` into `.env`
2. Fill out the `L1_RPC` and `PRIVATE_KEY_DEPLOYER` environment variables in `.env`
3. Run `npx hardhat deploy --network <network-name>` to deploy the L1 contracts
4. Run `npx hardhat etherscan-verify --network <network-name> --sleep` to verify contracts on Etherscan
Devnet and qanet use separate deployment records and generated configuration.
See [QA deployment and recovery](../ops/README.md#qa-deployment) for required RPC,
role, sequencer and delay settings. Use `make devnet-up` from the repository root
for the local container network.

In this directory, `yarn deploy:qanet` runs the existing
`scripts/localDeploy.sh --network qanet`: deploy L1 contracts, generate L2 genesis,
initialize contracts, register submitters and verify the result. The shell flow
sets `DOTENV_CONFIG_PATH=/dev/null`; supply inputs through the documented environment
variables or options. It does not start QA services.

`deploy:devnetL1`, `deploy:qanetL1`, `initialize:devnetL1`, `initialize:qanetL1`,
`register:devnetL1`, `register:qanetL1`, `verify:devnetL1` and `verify:qanetL1` each
run one stage. Their records are `../ops/l2-genesis/.devnet/devnetL1.json` and
`../ops/l2-genesis/.qanet/qanetL1.json`. Standalone initialization must receive the
generated `batchHeader` through `DEPLOY_CONFIG_OVERRIDE`, together with the same
role overrides used for deployment. Overrides may contain only existing fields of
the selected L1 configuration. Preserve records and generated files when retrying.
Older `contracts/devnetL1.json` or `contracts/qanetL1.json` files are not migrated or
deleted automatically; inspect their chain and addresses before choosing an output
directory.

## Tools

Expand Down
46 changes: 11 additions & 35 deletions contracts/deploy/010-ProxyAdmin.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,15 @@
import "@nomiclabs/hardhat-web3";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";

import {
HardhatRuntimeEnvironment
} from 'hardhat/types';
import { assertContractVariable, storage } from "../src/deploy-utils";
import {
ImplStorageName,
ProxyStorageName,
ContractFactoryName,
} from "../src/types"
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { assertContractVariable } from "../src/deploy-utils";
import { deployRecordedContract } from "../src/deployment-state";
import { ContractFactoryName, ImplStorageName } from "../src/types";

export const deployProxyAdmin = async (
hre: HardhatRuntimeEnvironment,
path: string,
deployer: any,
hre: HardhatRuntimeEnvironment, path: string, deployer: any
): Promise<string> => {
const ProxyAdminContractFactoryName = ContractFactoryName.ProxyAdmin
// @ts-ignore
const ProxyAdminImplStorageName = ImplStorageName.ProxyAdmin

const Factory = await hre.ethers.getContractFactory(ProxyAdminContractFactoryName)
const contract = await Factory.deploy()
await contract.deployed()
console.log("%s=%s ; TX_HASH: %s", ProxyAdminImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash);
// check params then storage
await assertContractVariable(contract, 'owner', await deployer.getAddress())
const blockNumber = await hre.ethers.provider.getBlockNumber()
console.log("BLOCK_NUMBER: %s", blockNumber)
let err = await storage(path, ProxyAdminImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0)
if (err != '') {
return err
}
return ''
}
const contract = await deployRecordedContract(hre, path, deployer,
ImplStorageName.ProxyAdmin, ContractFactoryName.ProxyAdmin, []);
await assertContractVariable(contract, "owner", await deployer.getAddress());
return "";
};

export default deployProxyAdmin
export default deployProxyAdmin;
42 changes: 10 additions & 32 deletions contracts/deploy/011-EmptyContract.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,15 @@
import "@nomiclabs/hardhat-web3";
import "@nomiclabs/hardhat-ethers";
import "@nomiclabs/hardhat-waffle";

import {
HardhatRuntimeEnvironment
} from 'hardhat/types';
import { storage } from "../src/deploy-utils";
import {
ImplStorageName,
ProxyStorageName,
ContractFactoryName,
} from "../src/types"
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { assertContractVariable } from "../src/deploy-utils";
import { deployRecordedContract } from "../src/deployment-state";
import { ContractFactoryName, ImplStorageName } from "../src/types";

export const deployEmptyContract = async (
hre: HardhatRuntimeEnvironment,
path: string,
deployer: any,
hre: HardhatRuntimeEnvironment, path: string, deployer: any
): Promise<string> => {
const EmptyContractFactoryName = ContractFactoryName.EmptyContract
const EmptyContractImplStorageName = ImplStorageName.EmptyContract
const contract = await deployRecordedContract(hre, path, deployer,
ImplStorageName.EmptyContract, ContractFactoryName.EmptyContract, []);

const Factory = await hre.ethers.getContractFactory(EmptyContractFactoryName)
const contract = await Factory.deploy()
await contract.deployed()
console.log("%s=%s ; TX_HASH: %s", EmptyContractImplStorageName, contract.address.toLocaleLowerCase(), contract.deployTransaction.hash);
// check params
const blockNumber = await hre.ethers.provider.getBlockNumber()
console.log("BLOCK_NUMBER: %s", blockNumber)
let err = await storage(path, EmptyContractImplStorageName, contract.address.toLocaleLowerCase(), blockNumber || 0)
if (err != '') {
return err
}
return ''
}
return "";
};

export default deployEmptyContract
export default deployEmptyContract;
Loading
Loading