Created
March 18, 2024 08:42
-
-
Save keroro520/5b9827f83ab9ba59a07fb1bf7000756a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Exit on error | |
set -e | |
# Check if required environment variables are set | |
required_vars=( | |
"L1_RPC" # L1's RPC | |
"OP_CHAIN_ID" # L2's chain id | |
"OP_NETWORK_NAME" # L2's nickname | |
"OP_DEPLOYER_PRIVKEY" # Deployer's private key | |
"OP_BATCHER_PRIVKEY" # Batcher's private key | |
"OP_BATCHER_ADDRESS" # Batcher's address | |
"OP_PROPOSER_PRIVKEY" # Proposer's private key | |
"OP_PROPOSER_ADDRESS" # Proposer's address | |
"OP_SEQUENCER_PRIVKEY" # Sequencer's private key | |
"OP_SEQUENCER_ADDRESS" # Sequencer's address | |
"OP_ADMIN_PRIVKEY" # Admin's private key | |
"OP_ADMIN_ADDRESS" # Admin's address | |
"OP_ROOT_DIR" # The path to ethereum-optimism/optimism repo | |
"OP_DATA_DIR" # The path to data/ directory | |
"OP_DEPLOY_CONFIG_FILE_TEMPLATE" # The path to deploy-config file template | |
) | |
for var in "${required_vars[@]}"; do | |
if [ -z "${!var}" ]; then | |
echo "ERROR: $var is required" | |
exit 1 | |
fi | |
done | |
# Clean the current environment | |
clean() { | |
# Clean Docker containeres | |
if docker ps --quiet --filter "name=l2" | grep -q '.'; then | |
docker stop l2 | |
fi | |
if docker ps --quiet --filter "name=op-node" | grep -q '.'; then | |
docker stop op-node | |
fi | |
if docker ps --quiet --filter "name=op-batcher" | grep -q '.'; then | |
docker stop op-batcher | |
fi | |
if docker ps --quiet --filter "name=op-proposer" | grep -q '.'; then | |
docker stop op-proposer | |
fi | |
# Clean data directory | |
rm -rf "$OP_DATA_DIR" | |
# Clean deploy config files | |
rm -rf "$OP_ROOT_DIR/packages/contracts-bedrock/deployments/$OP_NETWORK_NAME" | |
} | |
# Get the L1 chain id | |
l1_chain_id() { | |
curl "$L1_RPC" --silent -X POST --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":74}' -H 'Content-Type: application/json' \ | |
| jq '.result' \ | |
| xargs -I {} printf '%d' {} | |
} | |
# Get the L1 tip number and tip timestamp | |
l1_tip() { | |
local l1_tip_number l1_tip_timestamp | |
l1_tip_number=$(curl "$L1_RPC" --silent -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":74}' -H 'Content-Type: application/json' \ | |
| jq '.result') | |
l1_tip_timestamp=$(curl "$L1_RPC" --silent -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":['"$l1_tip_number"', false],"id":74}' -H 'Content-Type: application/json' \ | |
| jq '.result.timestamp') | |
echo "$l1_tip_number" "$l1_tip_timestamp" | |
} | |
# Generate deploy-config file based on template file | |
generate_deploy_config() { | |
local l1_chain_id_ l1_tip_number l1_tip_timestamp | |
l1_chain_id_=$(l1_chain_id) | |
l1_tip_number=$(l1_tip | awk '{print $1}') | |
l1_tip_timestamp=$(l1_tip | awk '{print $2}') | |
DEPLOY_CONFIG_TARGET="$OP_ROOT_DIR/packages/contracts-bedrock/deploy-config/$OP_NETWORK_NAME.json" | |
cat "$OP_DEPLOY_CONFIG_FILE_TEMPLATE" \ | |
| jq ".l1ChainID = $l1_chain_id_" \ | |
| jq ".l2ChainID = $OP_CHAIN_ID" \ | |
| jq ".l1StartingBlockTag = $l1_tip_number" \ | |
| jq ".l1GenesisBlockTimestamp = $l1_tip_timestamp" \ | |
| sed "s/OP_SEQUENCER_ADDRESS/$OP_SEQUENCER_ADDRESS/g" \ | |
| sed "s/OP_BATCHER_ADDRESS/$OP_BATCHER_ADDRESS/g" \ | |
| sed "s/OP_PROPOSER_ADDRESS/$OP_PROPOSER_ADDRESS/g" \ | |
| sed "s/OP_ADMIN_ADDRESS/$OP_ADMIN_ADDRESS/g" \ | |
| sed "s/OP_DEPLOYER_ADDRESS/$OP_DEPLOYER_ADDRESS/g" \ | |
| tee "$DEPLOY_CONFIG_TARGET" | |
} | |
# Deploy L1 contracts | |
deploy_contracts() { | |
pushd "$OP_ROOT_DIR/packages/contracts-bedrock" | |
CHAIN_ID=$(l1_chain_id) \ | |
L1_RPC="$L1_RPC" \ | |
PRIVATE_KEY_DEPLOYER="$OP_DEPLOYER_PRIVKEY" \ | |
yarn hardhat --network "$OP_NETWORK_NAME" deploy --tags l1 | |
popd | |
} | |
# Output addresses.json | |
# Output sdk-addresses.json | |
output_addresses_json() { | |
mkdir -p $OP_DATA_DIR | |
local DEPLOYMENT_DIR ADDRESSES_JSON SDK_ADDRESSES_JSON | |
DEPLOYMENT_DIR="$OP_ROOT_DIR/packages/contracts-bedrock/deployments/$OP_NETWORK_NAME" | |
ADDRESSES_JSON="$OP_DATA_DIR/addresses.json" | |
SDK_ADDRESSES_JSON="$OP_DATA_DIR/sdk-addresses.json" | |
CONTRACTS=$(ls -1 $DEPLOYMENT_DIR/*.json) | |
addresses="{}" | |
for contract_path in $(ls -1 $DEPLOYMENT_DIR/*.json); do | |
contract_name=$(echo $contract_path | xargs -I {} basename {} | awk -F '.' '{print $1}') | |
contract_addr=$(cat $contract_path | jq '.address') | |
addresses=$(echo $addresses | jq ".$contract_name = $contract_addr") | |
done | |
echo $addresses | jq | tee $ADDRESSES_JSON | |
sdk_addresses="{ | |
\"AddressManager\": \"0x0000000000000000000000000000000000000000\", | |
\"StateCommitmentChain\": \"0x0000000000000000000000000000000000000000\", | |
\"CanonicalTransactionChain\": \"0x0000000000000000000000000000000000000000\", | |
\"BondManager\": \"0x0000000000000000000000000000000000000000\", | |
\"L1CrossDomainMessenger\": $(echo $addresses | jq '.Proxy__OVM_L1CrossDomainMessenger'), | |
\"L1StandardBridge\": $(echo $addresses | jq '.Proxy__OVM_L1StandardBridge'), | |
\"OptimismPortal\": $(echo $addresses | jq '.OptimismPortalProxy'), | |
\"L2OutputOracle\": $(echo $addresses | jq '.L2OutputOracleProxy') | |
}" | |
echo $sdk_addresses | jq | tee $SDK_ADDRESSES_JSON | |
} | |
# Output rollup.json | |
output_rollup_json() { | |
cd $OP_ROOT_DIR/op-node/ | |
go run cmd/main.go genesis l2 \ | |
--l1-rpc $L1_RPC \ | |
--deploy-config $OP_ROOT_DIR/packages/contracts-bedrock/deploy-config/$OP_NETWORK_NAME.json \ | |
--deployment-dir $OP_ROOT_DIR/packages/contracts-bedrock/deployments/$OP_NETWORK_NAME \ | |
--outfile.l2 $OP_DATA_DIR/genesis-l2.json \ | |
--outfile.rollup $OP_DATA_DIR/rollup.json | |
} | |
# Output deployment files | |
save_deployment_info() { | |
mkdir -p $OP_DATA_DIR/deploy-config/ | |
cp -r \ | |
$OP_ROOT_DIR/packages/contracts-bedrock/deploy-config/$OP_NETWORK_NAME.json \ | |
$OP_DATA_DIR/deploy-config/$OP_NETWORK_NAME.json | |
mkdir -p $OP_DATA_DIR/deployments/ | |
cp -r \ | |
$OP_ROOT_DIR/packages/contracts-bedrock/deployments/$OP_NETWORK_NAME \ | |
$OP_DATA_DIR/deployments/$OP_NETWORK_NAME | |
if [[ -n "${CHAIN_INFRA_DIR}" ]]; then | |
# Only for OPS creating new networks | |
# | |
# We are going to create a PR to update these deployment files | |
mkdir -p "${CHAIN_INFRA_DIR}" | |
cp -r $OP_DATA_DIR/addresses.json \ | |
$OP_DATA_DIR/sdk-addresses.json \ | |
$OP_DATA_DIR/genesis-l2.json \ | |
$OP_DATA_DIR/rollup.json \ | |
$OP_DATA_DIR/deploy-config/ \ | |
$OP_DATA_DIR/deployments/ \ | |
$CHAIN_INFRA_DIR | |
if [[ -f "${CHAIN_INFRA_DIR}/values.yaml" ]]; then | |
new_l2oo_address=$(cat $OP_DATA_DIR/addresses.json | jq -r '.L2OutputOracleProxy') | |
old_l2oo_address=$(grep -A 1 'name: L2OO_ADDRESS' "${CHAIN_INFRA_DIR}/values.yaml" \ | |
| grep 'value: ' \ | |
| awk -F '"' '{print $2}') | |
gsed -i "s/$old_l2oo_address/$new_l2oo_address/g" "${CHAIN_INFRA_DIR}/values.yaml" | |
fi | |
fi | |
} | |
# Start OP-Geth Docker container | |
start_op_geth() { | |
docker run \ | |
--name l2 -d -it --rm \ | |
-p "9545:8545" \ | |
-p "8551:8551" \ | |
-v "$OP_DATA_DIR/db:/db" \ | |
-v "$OP_DATA_DIR/genesis-l2.json:/genesis.json" \ | |
-v "$OP_ROOT_DIR/ops-bedrock/test-jwt-secret.txt:/config/test-jwt-secret.txt" \ | |
l2geth:latest --authrpc.jwtsecret=/config/test-jwt-secret.txt | |
} | |
# Start OP-Node Docker container | |
start_op_node() { | |
docker run \ | |
--name op-node -d -it --rm \ | |
-p "7545:8545" \ | |
-p "9003:9003" \ | |
-p "7300:7300" \ | |
-p "6060:6060" \ | |
-v "$OP_ROOT_DIR/ops-bedrock/p2p-sequencer-key.txt:/config/p2p-sequencer-key.txt" \ | |
-v "$OP_ROOT_DIR/ops-bedrock/p2p-node-key.txt:/config/p2p-node-key.txt" \ | |
-v "$OP_ROOT_DIR/ops-bedrock/test-jwt-secret.txt:/config/test-jwt-secret.txt" \ | |
-v "$OP_DATA_DIR/rollup.json:/rollup.json" \ | |
-v "$OP_DATA_DIR/op_log:/op_log" \ | |
op-node:latest \ | |
op-node \ | |
--l1.trustrpc \ | |
--l1=$L1_RPC \ | |
--l2=http://host.docker.internal:8551 \ | |
--l2.jwt-secret=/config/test-jwt-secret.txt \ | |
--sequencer.enabled \ | |
--sequencer.l1-confs=0 \ | |
--verifier.l1-confs=0 \ | |
--p2p.sequencer.key="$OP_SEQUENCER_PRIVKEY" \ | |
--rollup.config=/rollup.json \ | |
--rpc.addr=0.0.0.0 \ | |
--rpc.port=8545 \ | |
--p2p.listen.ip=0.0.0.0 \ | |
--p2p.listen.tcp=9003 \ | |
--p2p.listen.udp=9003 \ | |
--snapshotlog.file=/op_log/snapshot.log \ | |
--p2p.priv.path=/config/p2p-node-key.txt \ | |
--metrics.enabled \ | |
--metrics.addr=0.0.0.0 \ | |
--metrics.port=7300 \ | |
--pprof.enabled \ | |
--rpc.enable-admin | |
} | |
# Start OP-Batcher | |
start_op_batcher() { | |
docker run \ | |
--name op-batcher -d -it --rm \ | |
-p "6061:6060" \ | |
-p "7301:7300" \ | |
--env OP_BATCHER_L1_ETH_RPC=$L1_RPC \ | |
--env OP_BATCHER_L2_ETH_RPC="http://host.docker.internal:9545" \ | |
--env OP_BATCHER_ROLLUP_RPC="http://host.docker.internal:7545" \ | |
--env OP_BATCHER_MAX_L1_TX_SIZE_BYTES=480000 \ | |
--env OP_BATCHER_TARGET_L1_TX_SIZE_BYTES=400000 \ | |
--env OP_BATCHER_TARGET_NUM_FRAMES=1 \ | |
--env OP_BATCHER_APPROX_COMPR_RATIO="1.0" \ | |
--env OP_BATCHER_CHANNEL_TIMEOUT=40 \ | |
--env OP_BATCHER_POLL_INTERVAL="10ms" \ | |
--env OP_BATCHER_NUM_CONFIRMATIONS=1 \ | |
--env OP_BATCHER_SAFE_ABORT_NONCE_TOO_LOW_COUNT=3 \ | |
--env OP_BATCHER_RESUBMISSION_TIMEOUT="30s" \ | |
--env OP_BATCHER_PRIVATE_KEY="${OP_BATCHER_PRIVKEY}" \ | |
--env OP_BATCHER_SEQUENCER_BATCH_INBOX_ADDRESS="0xff00000000000000000000000000000000000000" \ | |
--env OP_BATCHER_LOG_TERMINAL="true" \ | |
--env OP_BATCHER_PPROF_ENABLED="true" \ | |
--env OP_BATCHER_METRICS_ENABLED="true" \ | |
--env OP_BATCHER_SUB_SAFETY_MARGIN=20 \ | |
--env OP_BATCHER_LOG_LEVEL=debug \ | |
op-batcher:latest | |
} | |
# Start OP-Proposer | |
start_op_proposer() { | |
export L2OO_ADDRESS=$(cat $OP_DATA_DIR/addresses.json | jq '.L2OutputOracleProxy' | awk -F '"' '{print $2}') | |
docker run \ | |
--name op-proposer -d -it --rm \ | |
-p "6062:6060" \ | |
-p "7302:7300" \ | |
-e OP_PROPOSER_L1_ETH_RPC=$L1_RPC \ | |
-e OP_PROPOSER_ROLLUP_RPC="http://host.docker.internal:7545" \ | |
-e OP_PROPOSER_POLL_INTERVAL="1s" \ | |
-e OP_PROPOSER_NUM_CONFIRMATIONS=1 \ | |
-e OP_PROPOSER_SAFE_ABORT_NONCE_TOO_LOW_COUNT=3 \ | |
-e OP_PROPOSER_RESUBMISSION_TIMEOUT="30s" \ | |
-e OP_PROPOSER_LOG_TERMINAL="true" \ | |
-e OP_PROPOSER_L2OO_ADDRESS=${L2OO_ADDRESS} \ | |
-e OP_PROPOSER_PPROF_ENABLED="true" \ | |
-e OP_PROPOSER_METRICS_ENABLED="true" \ | |
-e OP_PROPOSER_ALLOW_NON_FINALIZED="true" \ | |
-e OP_PROPOSER_PRIVATE_KEY="${OP_PROPOSER_PRIVKEY}" \ | |
op-proposer:latest | |
} | |
# Build Docker images | |
build() { | |
pushd "$OP_ROOT_DIR" > /dev/null | |
docker build --file ops-bedrock/Dockerfile.l2 --tag l2geth:latest ops-bedrock | |
docker build --file op-node/Dockerfile --tag op-node:latest . | |
docker build --file op-batcher/Dockerfile --tag op-batcher:latest . | |
docker build --file op-proposer/Dockerfile --tag op-proposer:latest . | |
popd > /dev/null | |
} | |
# Main entry | |
main() { | |
if [ "$#" = "0" ]; then | |
echo "ERROR: subcommand is required" | |
exit 1 | |
fi | |
command="$1" | |
shift 1 | |
case $command in | |
"clean") | |
clean | |
;; | |
"deploy") | |
generate_deploy_config | |
deploy_contracts | |
output_addresses_json | |
output_rollup_json | |
save_deployment_info | |
;; | |
"build") | |
build | |
;; | |
"start") | |
start_op_geth && sleep 60 | |
start_op_node && sleep 10 | |
start_op_batcher | |
start_op_proposer | |
;; | |
"start_op_geth") | |
start_op_geth | |
;; | |
"start_op_node") | |
start_op_node | |
;; | |
"start_op_batcher") | |
start_op_batcher | |
;; | |
"start_op_proposer") | |
start_op_proposer | |
;; | |
*) | |
echo "ERROR: unknown subcommand \"$command\"" | |
exit 2 | |
;; | |
esac | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment