diff --git a/README.md b/README.md index e69de29..bbcd435 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,5 @@ +# eth-stack + +Stack to run a Ethereum node (geth + lighthouse beacon node) + +* [Stack documentation](./stack-orchestrator/stacks/eth/README.md) diff --git a/stack-orchestrator/compose/docker-compose-eth.yml b/stack-orchestrator/compose/docker-compose-eth.yml new file mode 100644 index 0000000..3237377 --- /dev/null +++ b/stack-orchestrator/compose/docker-compose-eth.yml @@ -0,0 +1,59 @@ +services: + eth-geth: + restart: on-failure + hostname: eth-geth + image: ethereum/client-go:alltools-v1.14.8 + environment: + CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG} + ETH_DATADIR: "/root/.ethereum" + CERC_NETWORK: ${CERC_NETWORK:-sepolia} + CERC_ALLOW_UNPROTECTED_TXS: ${CERC_ALLOW_UNPROTECTED_TXS:-false} + CERC_SYNCMODE: ${CERC_SYNCMODE:-full} + CERC_GCMODE: ${CERC_GCMODE:-archive} + CERC_GETH_VERBOSITY: ${CERC_GETH_VERBOSITY:-3} + entrypoint: ["sh", "-c"] + command: | + "/root/scripts/run-el.sh" + volumes: + - eth_geth_data:/root/.ethereum + - eth_secrets:/root/secrets + - ../config/eth/run-el.sh:/root/scripts/run-el.sh + healthcheck: + test: ["CMD", "nc", "-v", "localhost", "8545"] + interval: 30s + timeout: 10s + retries: 10 + start_period: 3s + ports: + - "8545" + - "8546" + - "6060" + - "30303/tcp" + - "30303/udp" + + eth-lighthouse: + restart: on-failure + hostname: eth-lighthouse + image: sigp/lighthouse:v5.3.0 + environment: + CERC_SCRIPT_DEBUG: ${CERC_SCRIPT_DEBUG} + ETH_ENDPOINT: "http://eth-geth:8545" + EXECUTION_ENDPOINT: "http://eth-geth:8551" + LIGHTHOUSE_DATADIR: "/root/.lighthouse" + CERC_NETWORK: ${CERC_NETWORK:-sepolia} + CERC_CHECKPOINT_SYNC_URL: ${CERC_CHECKPOINT_SYNC_URL} + CERC_DEBUG_LEVEL: ${CERC_DEBUG_LEVEL:-info} + command: bash /root/scripts/run-cl.sh + volumes: + - eth_lighthouse_data:/root/.lighthouse + - eth_secrets:/root/secrets + - ../config/eth/run-cl.sh:/root/scripts/run-cl.sh + ports: + - "8001" + - "9000/tcp" + - "9000/udp" + +volumes: + eth_geth_data: + eth_lighthouse_data: + eth_secrets: diff --git a/stack-orchestrator/config/eth/run-cl.sh b/stack-orchestrator/config/eth/run-cl.sh new file mode 100644 index 0000000..fbf9974 --- /dev/null +++ b/stack-orchestrator/config/eth/run-cl.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +set -e +set -o pipefail +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +echo "Using the following env:" +echo "CERC_NETWORK: ${CERC_NETWORK}" +echo "CERC_CHECKPOINT_SYNC_URL: ${CERC_CHECKPOINT_SYNC_URL}" +echo "CERC_DEBUG_LEVEL: ${CERC_DEBUG_LEVEL}" +echo "LIGHTHOUSE_DATADIR: ${LIGHTHOUSE_DATADIR}" +echo "ETH_ENDPOINT: ${ETH_ENDPOINT}" +echo "EXECUTION_ENDPOINT: ${EXECUTION_ENDPOINT}" + +# See https://linuxconfig.org/how-to-propagate-a-signal-to-child-processes-from-a-bash-script +cleanup() { + echo "Signal received, cleaning up..." + kill $(jobs -p) + + wait + echo "Done" +} +trap 'cleanup' SIGINT SIGTERM + +# Create a JWT secret at shared path if not found +jwtsecret_file_path=/root/secrets/jwtsecret +if [ ! -f "$jwtsecret_file_path" ]; then + openssl rand -hex 32 | tr -d "\n" > $jwtsecret_file_path + echo "Generated JWT secret at $jwtsecret_file_path" +fi + +http_port=8001 +lighthouse bn \ + --network $CERC_NETWORK \ + --datadir $LIGHTHOUSE_DATADIR/$CERC_NETWORK \ + --execution-endpoint $EXECUTION_ENDPOINT \ + --execution-jwt $jwtsecret_file_path \ + --checkpoint-sync-url $CERC_CHECKPOINT_SYNC_URL \ + --disable-deposit-contract-sync \ + --debug-level $CERC_DEBUG_LEVEL \ + --http \ + --http-address 0.0.0.0 \ + --http-port $http_port \ + 2>&1 | tee /var/log/lighthouse_bn.log & + +beacon_pid=$! +wait $beacon_pid diff --git a/stack-orchestrator/config/eth/run-el.sh b/stack-orchestrator/config/eth/run-el.sh new file mode 100755 index 0000000..efa2f60 --- /dev/null +++ b/stack-orchestrator/config/eth/run-el.sh @@ -0,0 +1,78 @@ +#!/bin/sh + +set -e +if [ -n "$CERC_SCRIPT_DEBUG" ]; then + set -x +fi + +echo "Using the following env:" +echo "CERC_NETWORK: ${CERC_NETWORK}" +echo "CERC_ALLOW_UNPROTECTED_TXS: ${CERC_ALLOW_UNPROTECTED_TXS}" +echo "CERC_SYNCMODE: ${CERC_SYNCMODE}" +echo "CERC_GCMODE: ${CERC_GCMODE}" +echo "CERC_GETH_VERBOSITY: ${CERC_GETH_VERBOSITY}" +echo "ETH_DATADIR: ${ETH_DATADIR}" + +# See https://linuxconfig.org/how-to-propagate-a-signal-to-child-processes-from-a-bash-script +cleanup() { + echo "Signal received, cleaning up..." + + # Kill the child process first (CERC_REMOTE_DEBUG=true uses dlv which starts geth as a child process) + pkill -P ${geth_pid} + sleep 2 + kill $(jobs -p) + + wait + echo "Done" +} +trap 'cleanup' SIGINT SIGTERM + +# Wait for the JWT secret to be generated +jwtsecret_file_path=/root/secrets/jwtsecret +retry_interval=3 +while [ ! -f "$jwtsecret_file_path" ]; do + echo "JWT secret not found, retrying after ${retry_interval}s..." + sleep $retry_interval +done + +echo "JWT secret found at $jwtsecret_file_path" + +NETWORK_OPT="" +if [ "$CERC_NETWORK" = "sepolia" ] || [ "$CERC_NETWORK" = "holesky" ] || [ "$CERC_NETWORK" = "mainnet" ]; then + NETWORK_OPT="--${CERC_NETWORK}" +else + NETWORK_OPT="--networkid ${CERC_NETWORK}" +fi + +OTHER_OPTS="" +if [ "$CERC_ALLOW_UNPROTECTED_TXS" == "true" ]; then + # Allow for unprotected (non EIP155) txs to be submitted via RPC + OTHER_OPTS="--rpc.allow-unprotected-txs" +fi + +geth \ + ${NETWORK_OPT} \ + --datadir="${ETH_DATADIR}" \ + --authrpc.addr="0.0.0.0" \ + --authrpc.vhosts="*" \ + --authrpc.jwtsecret="$jwtsecret_file_path" \ + --http \ + --http.addr="0.0.0.0" \ + --http.vhosts="*" \ + --http.api="eth,web3,net,admin,personal,debug" \ + --http.corsdomain="*" \ + --ws \ + --ws.addr="0.0.0.0" \ + --ws.origins="*" \ + --ws.api="eth,web3,net,admin,personal,debug" \ + --state.scheme hash \ + --gcmode $CERC_GCMODE \ + --syncmode=$CERC_SYNCMODE \ + --metrics \ + --metrics.addr="0.0.0.0" \ + --verbosity=${CERC_GETH_VERBOSITY} \ + ${OTHER_OPTS} \ + & + +geth_pid=$! +wait $geth_pid diff --git a/stack-orchestrator/stacks/eth/README.md b/stack-orchestrator/stacks/eth/README.md new file mode 100644 index 0000000..28f789b --- /dev/null +++ b/stack-orchestrator/stacks/eth/README.md @@ -0,0 +1,124 @@ +# eth + +## Setup + +* Clone the stack repo: + + ```bash + laconic-so fetch-stack git.vdb.to/cerc-io/eth-stack + ``` + +## Create a deployment + +* Create a spec file for the deployment, which will map the stack's ports and volumes to the host: + + ```bash + laconic-so --stack ~/cerc/eth-stack/stack-orchestrator/stacks/eth deploy init --output eth-spec.yml + ``` + +* Edit `network` in the spec file to map container ports to host ports as required: + + ```yml + ... + network: + ports: + eth-geth: + - '8545:8545' + - '8546:8546' + - '6060:6060' + - '30303:30303/tcp' + - '30303:30303/udp' + eth-lighthouse: + - '8001:8001' + - '9000:9000/tcp' + - '9000:9000/udp' + ``` + +* Create a deployment from the spec file: + + ```bash + laconic-so --stack ~/cerc/eth-stack/stack-orchestrator/stacks/eth deploy create --spec-file eth-spec.yml --deployment-dir eth-deployment + ``` + +* Inside the `eth-deployment` deployment directory, open `config.env` file and set following env variables: + + ```bash + # Optional + + # Network to run the ETH node for (default: sepolia) + # (one of mainnet, sepolia, holesky) + CERC_NETWORK= + + # Geth options (https://geth.ethereum.org/docs/fundamentals/command-line-options) + + # Allow unprotected txs (default: false) + CERC_ALLOW_UNPROTECTED_TXS= + + # Blockchain sync mode (default: full) + CERC_SYNCMODE= + + # Garbage collection mode (default: archive) + CERC_GCMODE= + + # Verbosity level (default: info) + CERC_GETH_VERBOSITY= + + # Lighthouse BN options (https://lighthouse-book.sigmaprime.io/help_bn.html) + + # Verbosity level (default: info) + CERC_DEBUG_LEVEL= + + # Required + + # Beacon node endpoint to use for checkpoint sync + # (https://eth-clients.github.io/checkpoint-sync-endpoints/) + CERC_CHECKPOINT_SYNC_URL= + ``` + +## Start + +* Start the deployment: + + ```bash + laconic-so deployment --dir eth-deployment start + ``` + +## Check status + +* To list down and monitor the running containers: + + ```bash + # With status + docker ps -a + + # Follow logs for eth-geth container + laconic-so deployment --dir eth-deployment logs eth-geth -f + + # Follow logs for eth-lighthouse container + laconic-so deployment --dir eth-deployment logs eth-lighthouse -f + ``` + +* Once the node has caught up to head, make a request to get the latest block number: + + ```bash + curl -H "Content-Type: application/json" \ + --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ + http://localhost:8545 + ``` + +## Clean up + +* To stop all services running in the background, while preserving chain data: + + ```bash + laconic-so deployment --dir eth-deployment stop + ``` + +* To stop all services and also delete chain data: + + ```bash + laconic-so deployment --dir eth-deployment stop --delete-volumes + + # Remove the deployment dir + sudo rm -rf eth-deployment + ``` diff --git a/stack-orchestrator/stacks/eth/stack.yml b/stack-orchestrator/stacks/eth/stack.yml new file mode 100644 index 0000000..b62c097 --- /dev/null +++ b/stack-orchestrator/stacks/eth/stack.yml @@ -0,0 +1,7 @@ +version: "1.0" +name: eth +description: "ETH stack" +repos: +containers: +pods: + - eth