diff --git a/.github/workflows/test-grandine-reth.yml b/.github/workflows/test-grandine-reth.yml new file mode 100644 index 00000000..628d5480 --- /dev/null +++ b/.github/workflows/test-grandine-reth.yml @@ -0,0 +1,55 @@ +name: Test Grandine and Reth +run-name: Test Grandine and Reth + +on: + push: + pull_request: + types: [opened, synchronize, labeled, unlabeled] + branches: [main] + +jobs: + test-grandine-reth: + if: | + contains(github.event.pull_request.labels.*.name, 'test-grandine') || + contains(github.event.pull_request.labels.*.name, 'test-all') || + github.event_name == 'push' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Create .env file + run: cp default.env .env + - name: Set Grandine/Reth + run: | + source ./.github/helper.sh + COMPOSE_FILE=grandine-allin1.yml:reth.yml + var=COMPOSE_FILE + set_value_in_env + FEE_RECIPIENT=0xDccf8451070a86183eE70D330C4c43b686E9CF86 + var=FEE_RECIPIENT + set_value_in_env + - name: Start Grandine/Reth + run: ./ethd up + - name: Pause for 30 seconds + run: sleep 30 + - name: Test Reth + run: ./.github/check-service.sh execution + - name: Test Grandine CL + run: ./.github/check-service.sh consensus +# - name: Test Grandine VC +# run: ./.github/check-service.sh validator + - name: Set Grandine/Reth w/ Lighthouse VC + run: | + source ./.github/helper.sh + COMPOSE_FILE=grandine-cl-only.yml:lighthouse-vc-only.yml:reth.yml + var=COMPOSE_FILE + set_value_in_env + - name: Start Grandine/Reth + run: ./ethd up + - name: Pause for 30 seconds + run: sleep 30 + - name: Test Reth + run: ./.github/check-service.sh execution + - name: Test Grandine CL + run: ./.github/check-service.sh consensus + - name: Test Lighthouse VC + run: ./.github/check-service.sh validator diff --git a/default.env b/default.env index 7c7f10b1..430c8b6b 100644 --- a/default.env +++ b/default.env @@ -228,6 +228,14 @@ LS_DOCKER_TAG=latest LS_DOCKER_REPO=chainsafe/lodestar LS_DOCKERFILE=Dockerfile.binary +# Grandine +# SRC build target can be a tag, a branch, or a pr as "pr-ID" +GRANDINE_SRC_BUILD_TARGET=master +GRANDINE_SRC_REPO=https://github.com/sifraitech/grandine +GRANDINE_DOCKER_TAG=latest +GRANDINE_DOCKER_REPO=sifrai/grandine +GRANDINE_DOCKERFILE=Dockerfile.binary + # Web3Signer W3S_DOCKER_TAG=latest W3S_DOCKER_REPO=consensys/web3signer @@ -297,4 +305,4 @@ DDNS_TAG=v2 NODE_EXPORTER_IGNORE_MOUNT_REGEX='^/(dev|proc|sys|run|var/lib/docker/.+)($|/)' # Used by ethd update - please do not adjust -ENV_VERSION=9 +ENV_VERSION=10 diff --git a/ethd b/ethd index d4437506..b48ce2e7 100755 --- a/ethd +++ b/ethd @@ -567,6 +567,13 @@ source_build() { docompose build --pull --no-cache consensus fi ;; + *grandine.yml* | *grandine-allin1.yml* | *grandine-cl-only.yml* ) + var="GRANDINE_DOCKERFILE" + build=$(sed -n -e "s/^${var}=\(.*\)/\1/p" "${ENV_FILE}" || true) + if [ "${build}" = "Dockerfile.source" ]; then + docompose build --pull --no-cache consensus + fi + ;; esac } @@ -968,6 +975,7 @@ envmigrate() { BESU_SRC_BUILD_TARGET BESU_SRC_REPO BESU_DOCKER_TAG BESU_DOCKER_REPO BESU_DOCKERFILE SSV_NODE_TAG \ DEPCLI_SRC_BUILD_TARGET DEPCLI_SRC_REPO DEPCLI_DOCKER_TAG W3S_DOCKER_TAG W3S_DOCKER_REPO \ PG_DOCKER_TAG RETH_SRC_BUILD_TARGET RETH_SRC_REPO RETH_DOCKER_TAG RETH_DOCKER_REPO RETH_DOCKERFILE \ + GRANDINE_SRC_BUILD_TARGET GRANDINE_SRC_REPO GRANDINE_DOCKER_TAG GRANDINE_DOCKER_REPO GRANDINE_DOCKERFILE \ SIREN_DOCKER_TAG SIREN_DOCKER_REPO NODE_EXPORTER_IGNORE_MOUNT_REGEX ) OLD_VARS=( LH_PORT PRYSM_WEB_PORT EC_NODE REWARDS_TO GETH_CACHE CF_API_TOKEN \ EC_HOST EC_LB EC_WS_HOST EC_WS_LB CC_HOST CC_LB EC_P2P_PORT CC_NODE CC_P2P_PORT EC_RPC_PORT EC_WS_PORT ) @@ -1373,6 +1381,7 @@ resync-consensus() { *lodestar.yml* | *lodestar-cl-only.yml* ) __cl_volume='lsconsensus-data'; __cl_client="lodestar";; *prysm.yml* ) __cl_volume='prysmbeacon-data'; __cl_client="prysm";; *prysm-cl-only.yml* ) __cl_volume='prysmconsensus-data'; __cl_client="prysm";; + *grandine-allin1.yml* ) __cl_volume='wipe-db'; __cl_client="grandine";; * ) echo "You do not appear to be running a consensus layer client. Nothing to do."; return;; esac @@ -1396,12 +1405,12 @@ resync-consensus() { * ) echo "Aborting."; exit 130;; esac - __cl_volume="$(basename "$(realpath .)")_${__cl_volume}" echo "Stopping ${__cl_client} container" docompose stop consensus && docompose rm -f consensus if [ "${__cl_volume}" = "wipe-db" ]; then docompose run --rm wipe-db else + __cl_volume="$(basename "$(realpath .)")_${__cl_volume}" dodocker volume rm "$(dodocker volume ls -q -f "name=${__cl_volume}")" fi echo @@ -2102,6 +2111,9 @@ __keys_usage() { echo " get-prysm-wallet" echo " Print Prysm's wallet password" echo + echo " get-grandine-wallet" + echo " Print Grandine's wallet password" + echo echo " prepare-address-change" echo " Create an offline-preparation.json with ethdo" echo " send-address-change" @@ -2556,19 +2568,19 @@ query_consensus_client() { "teku.yml" "Teku (Java) - consensus and validator client" \ "lodestar.yml" "Lodestar (Javascript) - consensus and validator client" \ "nimbus.yml" "Nimbus (Nim) - consensus and validator client" 3>&1 1>&2 2>&3) - elif [ "${NETWORK}" = "zhejiang" ]; then - CONSENSUS_CLIENT=lodestar.yml elif uname -a | grep -q aarch64; then CONSENSUS_CLIENT=$(whiptail --notags --title "Select consensus client" --menu \ "Which consensus client do you want to run?" 11 65 4 \ "nimbus.yml" "Nimbus (Nim) - consensus and validator client" \ + "grandine-allin1.yml" "Grandine (Rust) - consensus with built-in validator client" \ "lodestar.yml" "Lodestar (Javascript) - consensus and validator client" \ "lighthouse.yml" "Lighthouse (Rust) - consensus and validator client" \ "teku.yml" "Teku (Java) - consensus and validator client" 3>&1 1>&2 2>&3) else CONSENSUS_CLIENT=$(whiptail --notags --title "Select consensus client" --menu \ - "Which consensus client do you want to run?" 12 65 5 \ + "Which consensus client do you want to run?" 13 65 6 \ "teku.yml" "Teku (Java) - consensus and validator client" \ + "grandine-allin1.yml" "Grandine (Rust) - consensus with built-in validator client" \ "lighthouse.yml" "Lighthouse (Rust) - consensus and validator client" \ "nimbus.yml" "Nimbus (Nim) - consensus and validator client" \ "lodestar.yml" "Lodestar (Javascript) - consensus and validator client" \ @@ -2591,13 +2603,15 @@ query_consensus_only_client() { CONSENSUS_CLIENT=$(whiptail --notags --title "Select consensus client" --menu \ "Which consensus client do you want to run?" 11 65 4 \ "nimbus-cl-only.yml" "Nimbus (Nim) - consensus client" \ + "grandine-cl-only.yml" "Grandine (Rust) - consensus client" \ "lodestar-cl-only.yml" "Lodestar (Javascript) - consensus client" \ "lighthouse-cl-only.yml" "Lighthouse (Rust) - consensus client" \ "teku-cl-only.yml" "Teku (Java) - consensus client" 3>&1 1>&2 2>&3) else CONSENSUS_CLIENT=$(whiptail --notags --title "Select consensus client" --menu \ - "Which consensus client do you want to run?" 12 65 5 \ + "Which consensus client do you want to run?" 13 65 6 \ "teku-cl-only.yml" "Teku (Java) - consensus client" \ + "grandine-cl-only.yml" "Grandine (Rust) - consensus client" \ "lighthouse-cl-only.yml" "Lighthouse (Rust) - consensus client" \ "nimbus-cl-only.yml" "Nimbus (Nim) - consensus client" \ "lodestar-cl-only.yml" "Lodestar (Javascript) - consensus client" \ @@ -2975,11 +2989,13 @@ check_legacy() { # Literal match intended # shellcheck disable=SC2076 - if [[ "${value}" =~ "-allin1.yml" ]]; then + if [[ "${value}" =~ "-allin1.yml" && ! "${value}" =~ "grandine-allin1.yml" ]]; then # Warn re Grandine once VC if [[ "${value}" =~ "teku-allin1.yml" ]]; then __client="Teku" elif [[ "${value}" =~ "nimbus-allin1.yml" ]]; then __client="Nimbus" + elif [[ "${value}" =~ "grandine-allin1.yml" ]]; then + __client="Grandine" else __client="Mystery" fi @@ -3202,6 +3218,14 @@ version() { docompose exec validator /opt/teku/bin/teku --version echo ;;& + *grandine.yml* | *grandine-allin1.yml* | *grandine-cl-only* ) + docompose exec consensus grandine --version + echo + ;;& + *grandine-vc-only* ) + docompose exec validator grandine --version + echo + ;;& *geth.yml* ) docompose exec execution geth version echo @@ -3392,6 +3416,16 @@ if [ "${__old_compose}" -eq 1 ]; then exit 0 fi +if [ "${__old_docker}" -eq 1 ]; then + echo + echo "Docker version ${__docker_version} detected. This version is no longer supported." + echo "Please update to a current version. Supported versions can be seen at https://endoflife.date/docker-engine." + echo + echo "This should be as simple as \"sudo apt update && sudo apt dist-upgrade\" on Debian/Ubuntu" + echo "or updating Docker Desktop on macOS and Windows." + exit 0 +fi + if ! type -P whiptail >/dev/null 2>&1; then echo "Please install the package whiptail or newt before running ${__project_name}." exit 0 @@ -3430,12 +3464,3 @@ if [ "${__compose_upgraded}" -eq 1 ]; then echo "Optionally, you can switch to docker-ce." echo "Please see https://ethdocker.com/Usage/Prerequisites#switching-from-dockerio-to-docker-ce for instructions." fi - -if [ "${__old_docker}" -eq 1 ]; then - echo - echo "Docker version ${__docker_version} detected. This version no longer receives security updates." - echo "Please update to a current version." - echo - echo "This should be as simple as \"sudo apt update && sudo apt dist-upgrade\" on Debian/Ubuntu" - echo "or updating Docker Desktop on macOS and Windows." -fi diff --git a/grandine-allin1.yml b/grandine-allin1.yml new file mode 100644 index 00000000..e91249e6 --- /dev/null +++ b/grandine-allin1.yml @@ -0,0 +1,159 @@ +x-logging: &logging + logging: + driver: json-file + options: + max-size: 100m + max-file: "3" + tag: '{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}}' + + +x-build: &gr-build + context: ./grandine + dockerfile: ${GRANDINE_DOCKERFILE:-Dockerfile.binary} + args: + - BUILD_TARGET=${GRANDINE_SRC_BUILD_TARGET:-master} + - SRC_REPO=${GRANDINE_SRC_REPO:-https://github.com/sifraitech/grandine} + - DOCKER_TAG=${GRANDINE_DOCKER_TAG:-latest} + - DOCKER_REPO=${GRANDINE_DOCKER_REPO:-sifrai/grandine} + +services: + consensus: + restart: "unless-stopped" + build: + <<: *gr-build + image: grandine:${NETWORK} + pull_policy: never + user: gdconsensus + stop_grace_period: 1m + volumes: + - grandineconsensus-data:/var/lib/grandine + - /etc/localtime:/etc/localtime:ro + - jwtsecret:/var/lib/grandine/ee-secret + environment: + - RAPID_SYNC_URL=${RAPID_SYNC_URL} + - JWT_SECRET=${JWT_SECRET} + - MEV_BOOST=${MEV_BOOST} + - MEV_NODE=${MEV_NODE} + - BEACON_STATS_API=${BEACON_STATS_API} + - BEACON_STATS_MACHINE=${BEACON_STATS_MACHINE} + - EMBEDDED_VC=true + - CL_EXTRAS=${CL_EXTRAS:-} + - VC_EXTRAS=${VC_EXTRAS:-} + - GRAFFITI=${GRAFFITI:-} + - DEFAULT_GRAFFITI=${DEFAULT_GRAFFITI:-false} + - WEB3SIGNER=${WEB3SIGNER:-false} + - DOPPELGANGER=${DOPPELGANGER:-false} + - ARCHIVE_NODE=${ARCHIVE_NODE:-false} + - IPV6=${IPV6:-false} + - CL_P2P_PORT=${CL_P2P_PORT:-9000} + - CL_QUIC_PORT=${CL_QUIC_PORT:-9001} + - NETWORK=${NETWORK} + - RUST_LOG=${LOG_LEVEL:-info} + ports: + - ${HOST_IP:-}:${CL_P2P_PORT:-9000}:${CL_P2P_PORT:-9000}/tcp + - ${HOST_IP:-}:${CL_P2P_PORT:-9000}:${CL_P2P_PORT:-9000}/udp + - ${HOST_IP:-}:${CL_QUIC_PORT:-9001}:${CL_QUIC_PORT:-9001}/udp + networks: + default: + aliases: + - eth2 + <<: *logging + entrypoint: + - docker-entrypoint.sh + - grandine + - --disable-upnp + - --data-dir + - /var/lib/grandine + - --http-address + - 0.0.0.0 + - --http-port + - ${CL_REST_PORT:-5052} + - --http-allowed-origins=* + - --listen-address + - 0.0.0.0 + - --libp2p-port + - ${CL_P2P_PORT:-9000} + - --discovery-port + - ${CL_P2P_PORT:-9000} + - --quic-port + - ${CL_QUIC_PORT:-9001} + - --target-peers + - ${CL_MAX_PEER_COUNT:-80} + - --back-sync + - --eth1-rpc-urls + - ${EL_NODE} + - --jwt-secret + - /var/lib/grandine/ee-secret/jwtsecret + - --metrics + - --metrics-address + - 0.0.0.0 + - --metrics-port + - "8008" + - --suggested-fee-recipient + - ${FEE_RECIPIENT} + - --track-liveness + - --keystore-storage-password-file + - /var/lib/grandine/wallet-password.txt + - --enable-validator-api + - --validator-api-address + - 0.0.0.0 + - --validator-api-port + - ${KEY_API_PORT:-7500} + - --validator-api-allowed-origins=* + labels: + - metrics.scrape=true + - metrics.path=/metrics + - metrics.port=8008 + - metrics.instance=consensus + + wipe-db: + profiles: ["tools"] + restart: "no" + image: alpine:3 + user: "10002" + volumes: + - grandineconsensus-data:/var/lib/grandine + - /etc/localtime:/etc/localtime:ro + entrypoint: ["/bin/sh","-c"] + command: + - | + rm -rf /var/lib/grandine/${NETWORK}/beacon/sync + rm -rf /var/lib/grandine/${NETWORK}/beacon/eth1_cache + rm -rf /var/lib/grandine/${NETWORK}/beacon/beacon_fork_choice + + validator-keys: + profiles: ["tools"] + restart: "no" + build: + context: ./vc-utils + image: vc-utils:local + pull_policy: never + # The API token has 640 permissions. Root copies it, + # then switches to the local user's UID or if not provided, + # 1000. The UID has to be able to write .eth/validator_keys + # for the "keys delete" command. + user: root + volumes: + - grandineconsensus-data:/var/lib/grandine + - ./.eth/validator_keys:/validator_keys + - ./.eth/exit_messages:/exit_messages + - /etc/localtime:/etc/localtime:ro + environment: + - KEYSTORE_PASSWORD=${KEYSTORE_PASSWORD:-} + - KEY_API_PORT=${KEY_API_PORT:-7500} + - WEB3SIGNER=${WEB3SIGNER:-false} + - CL_NODE=${CL_NODE} + depends_on: + - consensus + entrypoint: + - keymanager.sh + - /var/lib/grandine/${NETWORK}/validator/api-token.txt + - consensus + +volumes: + grandineconsensus-data: + jwtsecret: + +networks: + default: + enable_ipv6: ${IPV6:-false} diff --git a/grandine-cl-only.yml b/grandine-cl-only.yml new file mode 100644 index 00000000..501e734d --- /dev/null +++ b/grandine-cl-only.yml @@ -0,0 +1,105 @@ +x-logging: &logging + logging: + driver: json-file + options: + max-size: 100m + max-file: "3" + tag: '{{.ImageName}}|{{.Name}}|{{.ImageFullID}}|{{.FullID}}' + + +x-build: &gr-build + context: ./grandine + dockerfile: ${GRANDINE_DOCKERFILE:-Dockerfile.binary} + args: + - BUILD_TARGET=${GRANDINE_SRC_BUILD_TARGET:-master} + - SRC_REPO=${GRANDINE_SRC_REPO:-https://github.com/sifraitech/grandine} + - DOCKER_TAG=${GRANDINE_DOCKER_TAG:-latest} + - DOCKER_REPO=${GRANDINE_DOCKER_REPO:-sifrai/grandine} + +services: + consensus: + restart: "unless-stopped" + build: + <<: *gr-build + image: grandine:${NETWORK} + pull_policy: build + user: gdconsensus + stop_grace_period: 1m + volumes: + - grandineconsensus-data:/var/lib/grandine + - /etc/localtime:/etc/localtime:ro + - jwtsecret:/var/lib/grandine/ee-secret + environment: + - RAPID_SYNC_URL=${RAPID_SYNC_URL} + - JWT_SECRET=${JWT_SECRET} + - MEV_BOOST=${MEV_BOOST} + - MEV_NODE=${MEV_NODE} + - BEACON_STATS_API=${BEACON_STATS_API} + - BEACON_STATS_MACHINE=${BEACON_STATS_MACHINE} + - CL_EXTRAS=${CL_EXTRAS:-} + - EMBEDDED_VC=false + - VC_EXTRAS= + - WEB3SIGNER=false + - DOPPELGANGER=false + - GRAFFITI= + - DEFAULT_GRAFFITI=true + - ARCHIVE_NODE=${ARCHIVE_NODE:-false} + - IPV6=${IPV6:-false} + - CL_P2P_PORT=${CL_P2P_PORT:-9000} + - CL_QUIC_PORT=${CL_QUIC_PORT:-9001} + - NETWORK=${NETWORK} + ports: + - ${HOST_IP:-}:${CL_P2P_PORT:-9000}:${CL_P2P_PORT:-9000}/tcp + - ${HOST_IP:-}:${CL_P2P_PORT:-9000}:${CL_P2P_PORT:-9000}/udp + - ${HOST_IP:-}:${CL_QUIC_PORT:-9001}:${CL_QUIC_PORT:-9001}/udp + networks: + default: + aliases: + - eth2 + <<: *logging + entrypoint: + - docker-entrypoint.sh + - grandine + - --disable-upnp + - --data-dir + - /var/lib/grandine + - --http-address + - 0.0.0.0 + - --http-port + - ${CL_REST_PORT:-5052} + - --http-allowed-origins=* + - --listen-address + - 0.0.0.0 + - --libp2p-port + - ${CL_P2P_PORT:-9000} + - --discovery-port + - ${CL_P2P_PORT:-9000} + - --quic-port + - ${CL_QUIC_PORT:-9001} + - --target-peers + - ${CL_MAX_PEER_COUNT:-80} + - --back-fill + - --eth1-rpc-urls + - ${EL_NODE} + - --jwt-secret + - /var/lib/grandine/ee-secret/jwtsecret + - --metrics + - --metrics-address + - 0.0.0.0 + - --metrics-port + - "8008" + - --suggested-fee-recipient + - ${FEE_RECIPIENT} + labels: + - metrics.scrape=true + - metrics.path=/metrics + - metrics.port=8008 + - metrics.instance=consensus + +volumes: + grandineconsensus-data: + jwtsecret: + +networks: + default: + enable_ipv6: ${IPV6:-false} diff --git a/grandine/Dockerfile.binary b/grandine/Dockerfile.binary new file mode 100644 index 00000000..bc026f03 --- /dev/null +++ b/grandine/Dockerfile.binary @@ -0,0 +1,40 @@ +ARG DOCKER_TAG +ARG DOCKER_REPO + +FROM ${DOCKER_REPO}:${DOCKER_TAG} + +# Unused, this is here to avoid build time complaints +ARG BUILD_TARGET +ARG SRC_REPO + +ARG USER=gdconsensus +ARG UID=10002 + +# See https://stackoverflow.com/a/55757473/12429735RUN +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + "${USER}" + +RUN set -eux; \ + apt-get update; \ + DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y gosu ca-certificates bash tzdata git curl; \ + rm -rf /var/lib/apt/lists/*; \ +# verify that the binary works + gosu nobody true + +# Create data mount point with permissions +RUN mkdir -p /var/lib/grandine/ee-secret && chown -R ${USER}:${USER} /var/lib/grandine \ +&& chmod -R 700 /var/lib/grandine && chmod 777 /var/lib/grandine/ee-secret +# Cannot assume buildkit, hence no chmod +COPY --chown=${USER}:${USER} ./docker-entrypoint.sh /usr/local/bin/ +# Belt and suspenders +RUN chmod -R 755 /usr/local/bin/* + +USER gdconsensus + +ENTRYPOINT ["grandine"] diff --git a/grandine/Dockerfile.source b/grandine/Dockerfile.source new file mode 100644 index 00000000..a67f6e2f --- /dev/null +++ b/grandine/Dockerfile.source @@ -0,0 +1,64 @@ +# Build Grandine in a stock Rust build container +FROM rust:bookworm as builder + +# Unused, this is here to avoid build time complaints +ARG DOCKER_TAG +ARG DOCKER_REPO + +ARG BUILD_TARGET +ARG SRC_REPO + +RUN apt-get update && apt-get -y dist-upgrade && apt-get install -y cmake libclang-dev protobuf-compiler libz-dev libssl-dev unzip + +WORKDIR /usr/src +RUN bash -c "git clone ${SRC_REPO} grandine && cd grandine && git config advice.detachedHead false \ +&& git fetch --all --tags \ +&& if [[ ${BUILD_TARGET} =~ pr-.+ ]]; then git fetch origin pull/$(echo ${BUILD_TARGET} | cut -d '-' -f 2)/head:gd-pr; git checkout gd-pr; else git checkout ${BUILD_TARGET}; fi \ +&& git submodule update --init dedicated_executor eth2_libp2p \ +&& cargo build --bin grandine --profile compact --features default-networks" + +# Pull all binaries into a second stage deploy debian container +FROM debian:bookworm-slim + +RUN set -eux; \ + apt-get update; \ + apt-get install -y gosu; \ + rm -rf /var/lib/apt/lists/*; \ +# verify that the binary works + gosu nobody true + +RUN apt-get update && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt-get install -y --no-install-recommends \ + libssl-dev \ + ca-certificates \ + wget \ + tzdata \ + git \ + curl \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +ARG USER=gdconsensus +ARG UID=10002 + +# See https://stackoverflow.com/a/55757473/12429735RUN +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + "${USER}" + +# Create data mount point with permissions +RUN mkdir -p /var/lib/grandine/ee-secret && chown -R ${USER}:${USER} /var/lib/grandine \ +&& chmod -R 700 /var/lib/grandine && chmod 777 /var/lib/grandine/ee-secret +# Cannot assume buildkit, hence no chmod +COPY --from=builder --chown=${USER}:${USER} /usr/src/grandine/target/compact/grandine /usr/local/bin/ +COPY --chown=${USER}:${USER} ./docker-entrypoint.sh /usr/local/bin/ +# Belt and suspenders +RUN chmod -R 755 /usr/local/bin/* + +USER gdconsensus + +ENTRYPOINT ["grandine"] diff --git a/grandine/docker-entrypoint.sh b/grandine/docker-entrypoint.sh new file mode 100755 index 00000000..8ed77efd --- /dev/null +++ b/grandine/docker-entrypoint.sh @@ -0,0 +1,123 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +if [ "$(id -u)" = '0' ]; then + chown -R gdconsensus:gdconsensus /var/lib/grandine + exec gosu gdconsensus docker-entrypoint.sh "$@" +fi + +if [ -n "${JWT_SECRET}" ]; then + echo -n "${JWT_SECRET}" > /var/lib/grandine/ee-secret/jwtsecret + echo "JWT secret was supplied in .env" +fi + +if [[ -O "/var/lib/grandine/ee-secret" ]]; then + # In case someone specificies JWT_SECRET but it's not a distributed setup + chmod 777 /var/lib/grandine/ee-secret +fi +if [[ -O "/var/lib/grandine/ee-secret/jwtsecret" ]]; then + chmod 666 /var/lib/grandine/ee-secret/jwtsecret +fi + +if [[ ! -f /var/lib/grandine/wallet-password.txt ]]; then + echo "Creating password for Grandine key wallet" + head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1 > /var/lib/grandine/wallet-password.txt +fi + +if [[ "${NETWORK}" =~ ^https?:// ]]; then + echo "Custom testnet at ${NETWORK}" + repo=$(awk -F'/tree/' '{print $1}' <<< "${NETWORK}") + branch=$(awk -F'/tree/' '{print $2}' <<< "${NETWORK}" | cut -d'/' -f1) + config_dir=$(awk -F'/tree/' '{print $2}' <<< "${NETWORK}" | cut -d'/' -f2-) + echo "This appears to be the ${repo} repo, branch ${branch} and config directory ${config_dir}." + # For want of something more amazing, let's just fail if git fails to pull this + set -e + if [ ! -d "/var/lib/grandine/testnet/${config_dir}" ]; then + mkdir -p /var/lib/grandine/testnet + cd /var/lib/grandine/testnet + git init --initial-branch="${branch}" + git remote add origin "${repo}" + git config core.sparseCheckout true + echo "${config_dir}" > .git/info/sparse-checkout + git pull origin "${branch}" + fi + bootnodes="$(paste -s -d, "/var/lib/grandine/testnet/${config_dir}/bootstrap_nodes.txt")" + set +e + __network="--configuration-directory=/var/lib/grandine/testnet/${config_dir} --boot-nodes=${bootnodes}" +else + __network="--network=${NETWORK}" +fi + +if [ "${ARCHIVE_NODE}" = "true" ]; then + echo "Grandine archive node without pruning" + __prune="--backfill" +else + __prune="" +fi + +# Check whether we should rapid sync +if [ -n "${RAPID_SYNC_URL}" ]; then + __rapid_sync="--checkpoint-sync-url=${RAPID_SYNC_URL}" + echo "Checkpoint sync enabled" +else + __rapid_sync="" +fi + +# Check whether we should send stats to beaconcha.in +if [ -n "${BEACON_STATS_API}" ]; then + __beacon_stats="--remote-metrics-url https://beaconcha.in/api/v1/client/metrics?apikey=${BEACON_STATS_API}&machine=${BEACON_STATS_MACHINE}" + echo "Beacon stats API enabled" +else + __beacon_stats="" +fi + +# Check whether we should use MEV Boost +if [ "${MEV_BOOST}" = "true" ]; then + __mev_boost="--builder-api-url ${MEV_NODE:-http://mev-boost:18550}" + echo "MEV Boost enabled" +else + __mev_boost="" +fi + +if [ "${IPV6}" = "true" ]; then + echo "Configuring Grandine to listen on IPv6 ports" + __ipv6="--listen-address-ipv6 :: --libp2p-port-ipv6 ${CL_P2P_PORT:-9000} --discovery-port-ipv6 ${CL_P2P_PORT:-9000} \ +--quic-port-ipv6 ${CL_QUIC_PORT:-9001}" +else + __ipv6="" +fi + +# Check whether we should enable doppelganger protection +if [ "${DOPPELGANGER}" = "true" ]; then + __doppel="" + echo "Doppelganger protection is not supported by Grandine" +else + __doppel="" +fi + + +# Web3signer URL +if [[ "${EMBEDDED_VC}" = "true" && "${WEB3SIGNER}" = "true" ]]; then + __w3s_url="--web3signer-urls http://web3signer:9000" +# while true; do +# if curl -s -m 5 http://web3signer:9000 &> /dev/null; then +# echo "web3signer is up, starting Grandine" +# break +# else +# echo "Waiting for web3signer to be reachable..." +# sleep 5 +# fi +# done +else + __w3s_url="" +fi + +if [ "${DEFAULT_GRAFFITI}" = "true" ]; then +# Word splitting is desired for the command line parameters +# shellcheck disable=SC2086 + exec "$@" ${__network} ${__w3s_url} ${__mev_boost} ${__rapid_sync} ${__prune} ${__beacon_stats} ${__ipv6} ${CL_EXTRAS} ${VC_EXTRAS} +else +# Word splitting is desired for the command line parameters +# shellcheck disable=SC2086 + exec "$@" ${__network} ${__w3s_url} ${__mev_boost} ${__rapid_sync} ${__prune} ${__beacon_stats} ${__ipv6} --graffiti "${GRAFFITI}" ${CL_EXTRAS} ${VC_EXTRAS} +fi diff --git a/teku-cl-only.yml b/teku-cl-only.yml index 99d7670a..a99a3f4b 100644 --- a/teku-cl-only.yml +++ b/teku-cl-only.yml @@ -37,13 +37,13 @@ services: - BEACON_STATS_API=${BEACON_STATS_API} - BEACON_STATS_MACHINE=${BEACON_STATS_MACHINE} - CL_EXTRAS=${CL_EXTRAS:-} + - EMBEDDED_VC=false - VC_EXTRAS= - DOPPELGANGER=false - - ARCHIVE_NODE=${ARCHIVE_NODE:-} - - GRAFFITI=${GRAFFITI:-} + - ARCHIVE_NODE=${ARCHIVE_NODE:-false} + - GRAFFITI= - DEFAULT_GRAFFITI=true - WEB3SIGNER=false - - EMBEDDED_VC=false - NETWORK=${NETWORK} ports: - ${HOST_IP:-}:${CL_P2P_PORT:-9000}:${CL_P2P_PORT:-9000}/tcp diff --git a/vc-utils/keymanager.sh b/vc-utils/keymanager.sh index 07e46e99..ced14839 100755 --- a/vc-utils/keymanager.sh +++ b/vc-utils/keymanager.sh @@ -142,6 +142,15 @@ get-prysm-wallet() { fi } +get-grandine-wallet() { + if [ -f /var/lib/grandine/wallet-password.txt ]; then + echo "The password for the Grandine wallet is:" + cat /var/lib/grandine/wallet-password.txt + else + echo "No stored password found for a Grandine wallet." + fi +} + recipient-get() { __check_pubkey "${__pubkey}" get-token @@ -1068,6 +1077,9 @@ usage() { echo " get-prysm-wallet" echo " Print Prysm's wallet password" echo + echo " get-grandine-wallet" + echo " Print Grandine's wallet password" + echo echo " prepare-address-change" echo " Create an offline-preparation.json with ethdo" echo " send-address-change" @@ -1112,6 +1124,10 @@ if [ "$(id -u)" = '0' ]; then get-prysm-wallet exit 0 ;; + get-grandine-wallet) + get-grandine-wallet + exit 0 + ;; esac if [ -z "$3" ]; then usage diff --git a/web3signer.yml b/web3signer.yml index 051ff0c6..080334cc 100644 --- a/web3signer.yml +++ b/web3signer.yml @@ -32,6 +32,7 @@ services: - --metrics-enabled - --metrics-host-allowlist=* - --http-host-allowlist=* + - --logging=${LOG_LEVEL:-info} - eth2 - --network=${NETWORK} - --enable-key-manager-api=true