Skip to content

Commit

Permalink
initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
MattKetmo committed Sep 11, 2023
0 parents commit 94ece1d
Show file tree
Hide file tree
Showing 22 changed files with 2,345 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.git
/.github
/Dockerfile
97 changes: 97 additions & 0 deletions .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Docker

on:
push:
branches:
- main
paths:
- 'pkg/**'
- '*.go'
- 'go.*'
- Dockerfile
- .github/workflows/docker.yaml
pull_request:
branches:
- main
paths:
- 'pkg/**'
- '*.go'
- 'go.*'
- Dockerfile
- .github/workflows/docker.yaml
release:
types:
- published

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: "Generate Build ID (main)"
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: |
branch=${GITHUB_REF##*/}
sha=${GITHUB_SHA::8}
ts=$(date +%s)
echo "BUILD_ID=${branch}-${ts}-${sha}" >> $GITHUB_ENV
- name: "Generate Build ID (PR)"
if: github.event_name == 'pull_request'
run: |
echo "BUILD_ID=pr-${{ github.event.number }}-$GITHUB_RUN_ID" >> $GITHUB_ENV
- name: "Generate Build ID (Release)"
if: github.event_name == 'release'
run: |
echo "BUILD_ID=${GITHUB_REF##*/}" >> $GITHUB_ENV
- name: 'Generate App Version'
run: echo "VERSION=$(make version)" >> $GITHUB_ENV

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=pr
type=ref,event=branch
type=raw,value=${{ env.BUILD_ID }}
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ env.VERSION }}
40 changes: 40 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Release

on:
release:
types: [published]

permissions:
contents: write
packages: write

jobs:
releases-matrix:
name: Release Go Binary
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: ["386", amd64, arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: arm64
goos: windows
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true

- uses: wangyoucao577/go-release-action@v1
env:
BUILD_FOLDER: .
with:
build_command: make build
extra_files: LICENSE README.md
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
goversion: "1.20"
md5sum: false
46 changes: 46 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Test

on:
push:
branches:
- main
paths:
- 'pkg/**'
- '*.go'
- 'go.*'
- '.github/workflows/test.yaml'
pull_request:
paths:
- 'pkg/**'
- '*.go'
- 'go.*'
- '.github/workflows/test.yaml'

env:
GO_VERSION: "1.20"

jobs:
unit-tests:
name: 'Unit tests'
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}

- id: go-cache-paths
run: |
echo "go-mod=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT
# Cache go mod cache to speedup deps downloads
- uses: actions/cache@v3
with:
path: ${{ steps.go-cache-paths.outputs.go-mod }}
key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-go-mod-

- name: 'Run unit tests'
run: |
make test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Builder
FROM golang:1.20-alpine3.18 as builder

ARG VERSION="0.0.0-build"
ENV VERSION=$VERSION

WORKDIR /go/src/app

RUN apk add --no-cache \
bash build-base git make

# Copy module files and download dependencies
COPY go.mod ./
COPY go.sum ./
RUN go mod download

COPY . ./

RUN make build

# Final
FROM alpine:3.18
RUN apk upgrade && apk add --no-cache bash curl

RUN addgroup -g 1001 app
RUN adduser -D -G app -u 1001 app

COPY --from=builder /go/src/app/build/cosmos-validator-watcher /

WORKDIR /
ENTRYPOINT ["/cosmos-validator-watcher"]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Kiln

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
BUILD_FOLDER ?= build
BINARY_NAME ?= cosmos-validator-watcher
PACKAGES ?= $(shell go list ./... | egrep -v "testutils" )
VERSION ?= $(shell git describe --tags)

.PHONY: build
build:
@go build -o $(BUILD_FOLDER)/$(BINARY_NAME) -v -ldflags="-X 'main.Version=$(VERSION)'"

.PHONY: test
test:
@go test -v $(PACKAGES)

.PHONY: version
version:
@echo $(VERSION)
134 changes: 134 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Cosmos Validator Watcher

[![License](https://img.shields.io/badge/license-MIT-blue)](https://opensource.org/licenses/MIT)

**Cosmos Validator Watcher** is a Prometheus exporter to help you monitor missed blocks on
any cosmos-based blockchains in real-time.

- Tracks for each block how many validators missed the signatures
- Tracks the current active set and check which validator is **bonded** or **jailed**
- Export all metrics (eg. missed blocks) on the `/metrics` endpoint


## ✨ Usage

Example for cosmoshub using 2 public RPC nodes and tracking 4 validators (with custom aliases).

![Cosmos Validator Watcher Screenshot](assets/cosmos-validator-watcher-output.jpg)

### Via compiled binary

Compiled binary can be found on the [Releases page](https://github.com/kilnfi/cosmos-validator-watcher/releases).

```bash
cosmos-validator-watcher \
--node https://cosmos-rpc.publicnode.com:443 \
--node https://cosmos-rpc.polkachu.com:443 \
--validator 3DC4DD610817606AD4A8F9D762A068A81E8741E2:kiln \
--validator 25445D0EB353E9050AB11EC6197D5DCB611986DB:allnodes \
--validator 9DF8E338C85E879BC84B0AAA28A08B431BD5B548:9df8e338 \
--validator ABC1239871ABDEBCDE761D718978169BCD019739:random-name
```

### Via Docker

Latest Docker image can be found on the [Packages page](https://github.com/kilnfi/cosmos-validator-watcher/pkgs/container/cosmos-validator-watcher).

```bash
docker run --rm ghcr.io/kilnfi/cosmos-validator-watcher:latest \
--node https://cosmos-rpc.publicnode.com:443 \
--node https://cosmos-rpc.polkachu.com:443 \
--validator 3DC4DD610817606AD4A8F9D762A068A81E8741E2:kiln \
--validator 25445D0EB353E9050AB11EC6197D5DCB611986DB:allnodes \
--validator 9DF8E338C85E879BC84B0AAA28A08B431BD5B548:9df8e338 \
--validator ABC1239871ABDEBCDE761D718978169BCD019739:random-name
```

### Available options

```
cosmos-validator-watcher --help
NAME:
covmos-validator-watcher - Cosmos Valdiator Watcher
USAGE:
covmos-validator-watcher [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--http-addr value http server address (default: ":8080")
--namespace value namespace for Prometheus metrics (default: "cosmos_validator_watcher")
--no-color disable colored output (default: false)
--node value [ --node value ] RPC node endpoint to connect to (speficied multiple nodes for high availability) (default: "http://localhost:26657")
--validator value [ --validator value ] validator address(es) to track (use :my-label to add a custom label in metrics & ouput)
--help, -h show help
--version, -v print the version
```


## ❇️ Endpoints

- `/metrics` exposed Prometheus metrics (see next section)
- `/ready` responds OK when at least one of the nodes is synced (ie. `.SyncInfo.catching_up` is `false`)
- `/live` responds OK as soon as server is up & running correctly


## 📊 Prometheus metrics

All metrics are by default prefixed by `cosmos_validator_watcher` but this can be changed through options.

Metrics (without prefix) | Description
----------------------------------------------|------------------------------------------------
`block_height` | Latest known block height (all nodes mixed up)
`validated_blocks` | Number of validated blocks per validator (for a bonded validator)
`missed_blocks` | Number of missed blocks per validator (for a bonded validator)
`tracked_blocks` | Number of blocks tracked since start
`skipped_blocks` | Number of blocks skipped (ie. not tracked) since start
`validator_bonded` | Set to 1 if the validator is bonded
`validator_jail` | Set to 1 if the validator is jailed
`node_block_height` | Latest fetched block height for each node
`node_synced` | Set to 1 is the node is synced (ie. not catching-up)


## ➡️ Flow

The app is structured in 2 parts:

- `watcher` connects to a Cosmos-based RPC node and watches for new blocks and the active validator set. You can use as many watchers as you want to ensure resilience and avoid missing any blocks.
- `exporter` receives data from all watchers, exposes Prometheus metrics, and writes status to standard output.

![Cosmos Validator Watcher Flow](assets/cosmos-validator-watcher-flow.png)


## ❓FAQ

### Which blockchains are compatible?

Any blockchains based on the cosmos-sdk should work:

- cosmoshub
- osmosis
- injective
- evmos
- persistence
- ...

This app is using the [CometBFT library](https://github.com/cometbft/cometbft/) (successor of Tendermint) as well as the `x/staking` module from the [Cosmos-SDK](https://github.com/cosmos/cosmos-sdk).

### How to get your validator pubkey address?

Use `tendermint show-validator` to get the pubkey and `debug pubkey` to convert to hex format.

```bash
gaiad debug pubkey "$(gaiad tendermint show-validator)" 2>&1 | grep "Address:" | awk '{print $2}'
```

(replace `gaiad` by the binary name or the desired chain, eg. `evmosd`, `strided`, `injectived`, …).


## 📃 License

[MIT License](LICENSE).
Binary file added assets/cosmos-validator-watcher-flow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/cosmos-validator-watcher-output.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 94ece1d

Please sign in to comment.