Skip to content

Instantly share code, notes, and snippets.

@keroro520
Created March 18, 2024 08:42
Show Gist options
  • Save keroro520/5b9827f83ab9ba59a07fb1bf7000756a to your computer and use it in GitHub Desktop.
Save keroro520/5b9827f83ab9ba59a07fb1bf7000756a to your computer and use it in GitHub Desktop.
#!/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