diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab131e7..e434749 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,16 +43,27 @@ jobs: fi echo "VERSION=$VERSION" >> $GITHUB_ENV - - name: Build and push + - name: Build and push (base) id: build uses: docker/build-push-action@v6 with: platforms: linux/amd64,linux/arm64 push: true + target: base provenance: mode=max tags: "${{ env.IMAGE_BASE }}:${{ env.VERSION }}" cache-from: type=registry,ref=${{ env.IMAGE_BASE }}:buildcache cache-to: type=registry,ref=${{ env.IMAGE_BASE }}:buildcache,mode=max + - name: Build and push (snapshotter) + uses: docker/build-push-action@v6 + with: + platforms: linux/amd64,linux/arm64 + push: true + target: base + provenance: mode=max + tags: "${{ env.IMAGE_BASE }}:${{ env.VERSION }}-snapshotter" + cache-from: type=registry,ref=${{ env.IMAGE_BASE }}:buildcache + cache-to: type=registry,ref=${{ env.IMAGE_BASE }}:buildcache,mode=max test: runs-on: buildjet-4vcpu-ubuntu-2204 needs: build @@ -62,7 +73,7 @@ jobs: run: docker run -d --name zetacored -p 8545:8545 -e MONIKER=$(uuidgen) ${{ needs.build.outputs.IMAGE }} - name: Wait for healthy run: | - while ! curl -s -f -X POST --data '{\"jsonrpc\":\"2.0\",\"method\":\"web3_clientVersion\",\"params\":[],\"id\":67}' -H 'Content-Type: application/json' http://localhost:8545; do + while ! curl -s -f --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":67}' -H 'Content-Type: application/json' http://localhost:8545; do if ! docker ps | grep zetacored; then echo "Container stopped?" exit 1 diff --git a/Dockerfile b/Dockerfile index cf21986..5837eb9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM golang:1.22.5-bookworm AS base-build RUN go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0 RUN go install github.com/hashicorp/go-getter/cmd/go-getter@v1.7.6 -FROM debian:bookworm +FROM debian:bookworm AS base RUN mkdir -p /root/.zetacored/cosmovisor/genesis/bin && \ ln -s /root/.zetacored/cosmovisor/genesis /root/.zetacored/cosmovisor/current @@ -16,8 +16,20 @@ RUN apt update && \ COPY --from=base-build /go/bin/cosmovisor /go/bin/go-getter /usr/local/bin -COPY run.sh /run.sh +COPY run.sh init.sh / VOLUME /root/.zetacored/data/ -ENTRYPOINT ["/run.sh"] \ No newline at end of file +ENTRYPOINT ["/run.sh"] + +FROM base AS snapshotter + +ARG TARGETARCH + +RUN apt update && \ + apt install -y rclone && \ + rm -rf /var/lib/apt/lists/* + +RUN ARCH=$( [ "$TARGETARCH" = "amd64" ] && echo "x86_64" || echo "$TARGETARCH" ) && \ + curl -L https://github.com/zeta-chain/cosmprund/releases/download/v0.2.0-zeta/cosmprund_Linux_${ARCH}.tar.gz | tar xz -C /usr/local/bin/ cosmprund &&\ + chmod +x /usr/local/bin/cosmprund \ No newline at end of file diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..5437bb6 --- /dev/null +++ b/init.sh @@ -0,0 +1,114 @@ +#!/bin/bash + +set -eo pipefail + +if [[ -n $DEBUG ]]; then + set -x +fi + +# script variables +ZETACHAIN_NETWORK=${ZETACHAIN_NETWORK:-"mainnet"} +ZETACHAIN_SNAPSHOT_TYPE=${ZETACHAIN_SNAPSHOT_TYPE:-"fullnode"} +ZETACORED_BINARY_URL=${ZETACORED_BINARY_URL:-} +MY_IP=${MY_IP:-$(curl -s https://checkip.amazonaws.com)} + +# script constants +CURL="curl -s -L --fail --retry 5 --retry-delay 2 --retry-max-time 10 -C -" + +if [[ -z $MONIKER ]]; then + echo '$MONIKER is required' + exit 1 +fi + +if [[ "$ZETACHAIN_NETWORK" == "mainnet" ]]; then + echo "Running mainnet" + ZETACHAIN_INIT_API_URL=${ZETACHAIN_INIT_API_URL:-"https://zetachain-mainnet.g.allthatnode.com/archive/rest"} + ZETACHAIN_SNAPSHOT_METADATA_URL=${ZETACHAIN_SNAPSHOT_METADATA_URL:-"https://snapshots.rpc.zetachain.com/mainnet/${ZETACHAIN_SNAPSHOT_TYPE}/latest.json"} + ZETACHAIN_NETWORK_CONFIG_URL_BASE=${ZETACHAIN_NETWORK_CONFIG_URL_BASE:-"https://raw.githubusercontent.com/zeta-chain/network-config/main/mainnet"} +elif [[ "$ZETACHAIN_NETWORK" == "testnet" || "$ZETACHAIN_NETWORK" == "athens3" ]]; then + echo "Running testnet" + ZETACHAIN_INIT_API_URL=${ZETACHAIN_INIT_API_URL:-"https://zetachain-athens.g.allthatnode.com/archive/rest"} + ZETACHAIN_SNAPSHOT_METADATA_URL=${ZETACHAIN_SNAPSHOT_METADATA_URL:-"https://snapshots.rpc.zetachain.com/testnet/${ZETACHAIN_SNAPSHOT_TYPE}/latest.json"} + ZETACHAIN_NETWORK_CONFIG_URL_BASE=${ZETACHAIN_NETWORK_CONFIG_URL_BASE:-"https://raw.githubusercontent.com/zeta-chain/network-config/main/athens3"} +else + echo "Invalid network" + exit 1 +fi + +# convert uname arch to goarch style +UNAME_ARCH=$(uname -m) +case "$UNAME_ARCH" in + x86_64) GOARCH=amd64;; + i686) GOARCH=386;; + armv7l) GOARCH=arm;; + aarch64) GOARCH=arm64;; + *) GOARCH=unknown;; +esac + +download_configs() { + echo "Downloading configs if they are not present" + mkdir -p .zetacored/config/ + mkdir -p .zetacored/data/ + if [[ ! -f .zetacored/config/app.toml ]]; then + $CURL -o .zetacored/config/app.toml "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/app.toml" + fi + if [[ ! -f .zetacored/config/config.toml ]]; then + $CURL -o .zetacored/config/config.toml "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/config.toml" + sed -i -e "s/^moniker = .*/moniker = \"${MONIKER}\"/" .zetacored/config/config.toml + fi + if [[ ! -f .zetacored/config/client.toml ]]; then + $CURL -o .zetacored/config/client.toml "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/client.toml" + fi + if [[ ! -f .zetacored/config/genesis.json ]]; then + $CURL -o .zetacored/config/genesis.json "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/genesis.json" + fi +} + +install_genesis_zetacored() { + echo "Installing genesis zetacored" + if [[ -z $ZETACORED_BINARY_URL ]]; then + max_height=$($CURL "${ZETACHAIN_SNAPSHOT_METADATA_URL}" | jq -r '.snapshots[0].height') + echo "Getting latest passed upgrade plan before ${max_height}" + $CURL "${ZETACHAIN_INIT_API_URL}/cosmos/gov/v1/proposals?pagination.reverse=true" | jq --arg max_height "$max_height" ' + .proposals[] | + select(.status == "PROPOSAL_STATUS_PASSED") | + .messages[] | + select(."@type" == "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade" and (.plan.height | + tonumber < $max_height))' | jq -s '.[0]' | tee /tmp/init-upgrade-plan.json + + ZETACORED_BINARY_URL=$(jq -r '.plan.info' /tmp/init-upgrade-plan.json | jq -r ".binaries[\"linux/$GOARCH\"]") + fi + # go-getter will verify the checksum of the downloaded binary + go-getter --mode file "$ZETACORED_BINARY_URL" .zetacored/cosmovisor/genesis/bin/zetacored + chmod +x .zetacored/cosmovisor/genesis/bin/zetacored + + # run the zetacored version to ensure it's the correct architecture, glibc version, and is in PATH + zetacored version +} + +restore_snapshot() { + snapshot_link=$($CURL "${ZETACHAIN_SNAPSHOT_METADATA_URL}" | jq -r '.snapshots[0].link') + echo "Restoring snapshot from ${snapshot_link}" + $CURL "$snapshot_link" | tar x -C $HOME/.zetacored +} + +cd $HOME + +if [[ -f /root/init_started && ! -f /root/init_completed ]]; then + echo "Initialization interrupted, resetting node data" + rm -rf .zetacored/data/* +fi + +touch /root/init_started +if [[ ! -f /root/init_complete ]]; then + echo "Starting initialization" + download_configs + install_genesis_zetacored + restore_snapshot + touch /root/init_complete +else + echo "Initialization already completed" +fi + +# always set IP address as it may change after restart +sed -i -e "s/^external_address = .*/external_address = \"${MY_IP}:26656\"/" .zetacored/config/config.toml \ No newline at end of file diff --git a/run.sh b/run.sh index 3a81dd0..46e3a3d 100755 --- a/run.sh +++ b/run.sh @@ -6,11 +6,7 @@ if [[ -n $DEBUG ]]; then set -x fi -# script variables -ZETACHAIN_NETWORK=${ZETACHAIN_NETWORK:-"mainnet"} -ZETACHAIN_SNAPSHOT_TYPE=${ZETACHAIN_SNAPSHOT_TYPE:-"fullnode"} -ZETACORED_BINARY_URL=${ZETACORED_BINARY_URL:-} -MY_IP=${MY_IP:-$(curl -s https://checkip.amazonaws.com)} +"$(dirname "$0")/init.sh" # cosmovisor variables export DAEMON_ALLOW_DOWNLOAD_BINARIES=${DAEMON_ALLOW_DOWNLOAD_BINARIES:-"true"} @@ -19,103 +15,5 @@ export DAEMON_NAME="zetacored" export DAEMON_HOME="$HOME/.zetacored" export UNSAFE_SKIP_BACKUP=true -# script constants -CURL="curl -s -L --fail --retry 5 --retry-delay 2 --retry-max-time 10" - -if [[ -z $MONIKER ]]; then - echo '$MONIKER is required' - exit 1 -fi - -if [[ "$ZETACHAIN_NETWORK" == "mainnet" ]]; then - echo "Running mainnet" - ZETACHAIN_INIT_API_URL=${ZETACHAIN_INIT_API_URL:-"https://zetachain-mainnet.g.allthatnode.com/archive/rest"} - ZETACHAIN_SNAPSHOT_METADATA_URL=${ZETACHAIN_SNAPSHOT_METADATA_URL:-"https://snapshots.rpc.zetachain.com/mainnet/${ZETACHAIN_SNAPSHOT_TYPE}/latest.json"} - ZETACHAIN_NETWORK_CONFIG_URL_BASE=${ZETACHAIN_NETWORK_CONFIG_URL_BASE:-"https://raw.githubusercontent.com/zeta-chain/network-config/main/mainnet"} -elif [[ "$ZETACHAIN_NETWORK" == "testnet" || "$ZETACHAIN_NETWORK" == "athens3" ]]; then - echo "Running testnet" - ZETACHAIN_INIT_API_URL=${ZETACHAIN_INIT_API_URL:-"https://zetachain-athens.g.allthatnode.com/archive/rest"} - ZETACHAIN_SNAPSHOT_METADATA_URL=${ZETACHAIN_SNAPSHOT_METADATA_URL:-"https://snapshots.rpc.zetachain.com/testnet/${ZETACHAIN_SNAPSHOT_TYPE}/latest.json"} - ZETACHAIN_NETWORK_CONFIG_URL_BASE=${ZETACHAIN_NETWORK_CONFIG_URL_BASE:-"https://raw.githubusercontent.com/zeta-chain/network-config/main/athens3"} -else - echo "Invalid network" - exit 1 -fi - -# convert uname arch to goarch style -UNAME_ARCH=$(uname -m) -case "$UNAME_ARCH" in - x86_64) GOARCH=amd64;; - i686) GOARCH=386;; - armv7l) GOARCH=arm;; - aarch64) GOARCH=arm64;; - *) GOARCH=unknown;; -esac - -download_configs() { - echo "Downloading configs if they are not present" - mkdir -p .zetacored/config/ - mkdir -p .zetacored/data/ - if [[ ! -f .zetacored/config/app.toml ]]; then - $CURL -o .zetacored/config/app.toml "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/app.toml" - fi - if [[ ! -f .zetacored/config/config.toml ]]; then - $CURL -o .zetacored/config/config.toml "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/config.toml" - sed -i -e "s/^moniker = .*/moniker = \"${MONIKER}\"/" .zetacored/config/config.toml - fi - if [[ ! -f .zetacored/config/genesis.json ]]; then - $CURL -o .zetacored/config/genesis.json "${ZETACHAIN_NETWORK_CONFIG_URL_BASE}/genesis.json" - fi -} - -install_genesis_zetacored() { - echo "Installing genesis zetacored" - if [[ -z $ZETACORED_BINARY_URL ]]; then - max_height=$($CURL "${ZETACHAIN_SNAPSHOT_METADATA_URL}" | jq -r '.snapshots[0].height') - echo "Getting latest passed upgrade plan before ${max_height}" - $CURL "${ZETACHAIN_INIT_API_URL}/cosmos/gov/v1/proposals?pagination.reverse=true" | jq --arg max_height "$max_height" ' - .proposals[] | - select(.status == "PROPOSAL_STATUS_PASSED") | - .messages[] | - select(."@type" == "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade" and (.plan.height | - tonumber < $max_height))' | jq -s '.[0]' | tee /tmp/init-upgrade-plan.json - - ZETACORED_BINARY_URL=$(jq -r '.plan.info' /tmp/init-upgrade-plan.json | jq -r ".binaries[\"linux/$GOARCH\"]") - fi - # go-getter will verify the checksum of the downloaded binary - go-getter --mode file "$ZETACORED_BINARY_URL" .zetacored/cosmovisor/genesis/bin/zetacored - chmod +x .zetacored/cosmovisor/genesis/bin/zetacored - - # run the zetacored version to ensure it's the correct architecture, glibc version, and is in PATH - zetacored version -} - -restore_snapshot() { - snapshot_link=$($CURL "${ZETACHAIN_SNAPSHOT_METADATA_URL}" | jq -r '.snapshots[0].link') - echo "Restoring snapshot from ${snapshot_link}" - $CURL "$snapshot_link" | tar x -C $HOME/.zetacored -} - -cd $HOME - -if [[ -f /root/init_started && ! -f /root/init_completed ]]; then - echo "Initialization interrupted, resetting node data" - rm -rf .zetacored/data/* -fi - -touch /root/init_started -if [[ ! -f /root/init_complete ]]; then - echo "Starting initialization" - download_configs - install_genesis_zetacored - restore_snapshot - touch /root/init_complete -else - echo "Initialization already completed" -fi - -# always set IP address as it may change after restart -sed -i -e "s/^external_address = .*/external_address = \"${MY_IP}:26656\"/" .zetacored/config/config.toml - # shellcheck disable=SC2068 exec cosmovisor run start --moniker "$MONIKER" $@ \ No newline at end of file