diff --git a/.github/release.config.js b/.github/release.config.js index 266d1c3..d028a57 100644 --- a/.github/release.config.js +++ b/.github/release.config.js @@ -18,10 +18,10 @@ module.exports = { }], ['@semantic-release/github', { assets: [ - { path: 'release/berachain-exporter-linux-amd64' }, - { path: 'release/berachain-exporter-darwin-amd64' }, - { path: 'release/berachain-exporter-darwin-arm64' }, - { path: 'release/berachain-exporter-windows-amd64.exe' } + { path: 'release/evm-exporter-linux-amd64' }, + { path: 'release/evm-exporter-darwin-amd64' }, + { path: 'release/evm-exporter-darwin-arm64' }, + { path: 'release/evm-exporter-windows-amd64.exe' } ], }], ['@semantic-release/git', { diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2284f50..1a07da6 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,37 +1,55 @@ -name: Build and Package +name: Build on: pull_request: - paths: - - '**.go' - - 'go.mod' - - 'go.sum' + branches: [ main ] jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + - name: Run tests + run: go test -v ./... + build: + needs: test + name: Build and Publish runs-on: ubuntu-latest permissions: contents: read packages: write steps: - - name: Checkout - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Setup Go + - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.22.1' + go-version: '1.21' + + - name: Get short SHA + id: sha + run: echo "sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Build Binaries + - name: Build binaries run: | mkdir -p release - GOOS=linux GOARCH=amd64 go build -o release/berachain-exporter-linux-amd64 - GOOS=darwin GOARCH=amd64 go build -o release/berachain-exporter-darwin-amd64 - GOOS=darwin GOARCH=arm64 go build -o release/berachain-exporter-darwin-arm64 - GOOS=windows GOARCH=amd64 go build -o release/berachain-exporter-windows-amd64.exe + GOOS=linux GOARCH=amd64 go build -o release/evm-exporter-linux-amd64-${{ steps.sha.outputs.sha }} + GOOS=darwin GOARCH=amd64 go build -o release/evm-exporter-darwin-amd64-${{ steps.sha.outputs.sha }} + GOOS=darwin GOARCH=arm64 go build -o release/evm-exporter-darwin-arm64-${{ steps.sha.outputs.sha }} + GOOS=windows GOARCH=amd64 go build -o release/evm-exporter-windows-amd64-${{ steps.sha.outputs.sha }}.exe - - name: Upload to GitHub Packages - uses: actions/upload-artifact@v3 - with: - name: binaries - path: release/* + - name: Publish to GitHub Packages + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + for file in release/*; do + gh release upload "commit-${{ steps.sha.outputs.sha }}" "$file" --clobber || gh release create "commit-${{ steps.sha.outputs.sha }}" "$file" + done diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 679d53b..f121214 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,7 +10,22 @@ on: - 'go.sum' jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.22.1' + + - name: Run tests + run: go test -v ./... + release: + needs: test runs-on: ubuntu-latest permissions: contents: write @@ -38,10 +53,10 @@ jobs: - name: Build Binaries run: | mkdir -p release - GOOS=linux GOARCH=amd64 go build -o release/berachain-exporter-linux-amd64 - GOOS=darwin GOARCH=amd64 go build -o release/berachain-exporter-darwin-amd64 - GOOS=darwin GOARCH=arm64 go build -o release/berachain-exporter-darwin-arm64 - GOOS=windows GOARCH=amd64 go build -o release/berachain-exporter-windows-amd64.exe + GOOS=linux GOARCH=amd64 go build -o release/evm-exporter-linux-amd64 + GOOS=darwin GOARCH=amd64 go build -o release/evm-exporter-darwin-amd64 + GOOS=darwin GOARCH=arm64 go build -o release/evm-exporter-darwin-arm64 + GOOS=windows GOARCH=amd64 go build -o release/evm-exporter-windows-amd64.exe - name: Install semantic-release run: npm install @semantic-release/git @semantic-release/changelog @semantic-release/exec conventional-changelog-conventionalcommits diff --git a/README.md b/README.md index d5f7caf..85de8bc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Berachain Block Exporter +# Cosmos EVM Exporter -A monitoring tool that tracks block production on Berachain's consensus and execution layers, specifically designed to monitor validator block proposals and their corresponding execution blocks. +A monitoring tool that tracks block production on Cosmos EVM chains consensus and execution layers, specifically designed to monitor validator block proposals and their corresponding execution blocks. ## Features @@ -46,10 +46,10 @@ enable_stdout = true # Enable console logging go run main.go --config=./config.toml # Build binary -go build -o berachain-exporter +go build -o evm-exporter # Run binary -./berachain-exporter --config=./config.toml +./evm-exporter --config=./config.toml ``` ## Metrics Endpoint @@ -59,7 +59,7 @@ Prometheus metrics are available at `http://localhost:2113/metrics` ## Requirements - Go 1.22.1 or later -- Access to Berachain RPC endpoints +- Access to Cosmos & Ethereum RPC endpoints ## Dependencies @@ -79,7 +79,7 @@ git clone [repository-url] go mod download # Build -go build -o berachain-exporter +go build -o evm-exporter ``` ## Example Output diff --git a/go.mod b/go.mod index c6e1456..328517d 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module blockchecker +module evm-exporter go 1.22.1 diff --git a/main.go b/main.go index 02e9a38..60db371 100755 --- a/main.go +++ b/main.go @@ -24,19 +24,21 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" ) +// Config defines the application's configuration parameters loaded from TOML type Config struct { - EVMAddress string `toml:"evm_address"` - TargetValidator string `toml:"target_validator"` - RPCEndpoint string `toml:"rpc_endpoint"` - LogFile string `toml:"log_file"` - MetricsPort string `toml:"metrics_port"` - EnableFileLog bool `toml:"enable_file_log"` - EnableStdout bool `toml:"enable_stdout"` - ETHEndpoint string `toml:"eth_endpoint"` + EVMAddress string `toml:"evm_address"` // Address of the EVM contract + TargetValidator string `toml:"target_validator"` // Validator address to monitor + RPCEndpoint string `toml:"rpc_endpoint"` // Consensus layer RPC endpoint + LogFile string `toml:"log_file"` // Path to log file + MetricsPort string `toml:"metrics_port"` // Prometheus metrics port + EnableFileLog bool `toml:"enable_file_log"` // Enable logging to file + EnableStdout bool `toml:"enable_stdout"` // Enable logging to stdout + ETHEndpoint string `toml:"eth_endpoint"` // Execution layer endpoint } var config *Config +// loadConfig reads and validates the configuration from the specified path func loadConfig(path string) (*Config, error) { var cfg Config if _, err := toml.DecodeFile(path, &cfg); err != nil { @@ -63,16 +65,17 @@ func loadConfig(path string) (*Config, error) { return &cfg, nil } +// Prometheus metrics for monitoring validator performance and block processing var ( blockMetrics = struct { - totalProposed prometheus.Counter - executionConfirmed prometheus.Counter - executionMissed prometheus.Counter - emptyConsensusBlocks prometheus.Counter - emptyExecutionBlocks prometheus.Counter - errors prometheus.Counter - currentHeight prometheus.Gauge - elToClGap prometheus.Gauge // New metric to track the gap + totalProposed prometheus.Counter // Total blocks proposed by validator + executionConfirmed prometheus.Counter // Blocks confirmed on execution layer + executionMissed prometheus.Counter // Blocks missed on execution layer + emptyConsensusBlocks prometheus.Counter // Blocks with no transactions + emptyExecutionBlocks prometheus.Counter // Empty blocks on execution layer + errors prometheus.Counter // Block processing errors + currentHeight prometheus.Gauge // Current block height + elToClGap prometheus.Gauge // Gap between execution and consensus layers }{ totalProposed: promauto.NewCounter(prometheus.CounterOpts{ Name: "validator_total_blocks_proposed", @@ -109,7 +112,7 @@ var ( } ) -// Status response structures +// StatusResponse defines the structure for consensus layer status API responses type StatusResponse struct { Result struct { SyncInfo struct { @@ -118,7 +121,7 @@ type StatusResponse struct { } `json:"result"` } -// Block response structures +// BlockResponse defines the structure for consensus layer block API responses type BlockResponse struct { JsonRPC string `json:"jsonrpc"` ID int `json:"id"` @@ -183,8 +186,8 @@ type BlockResponse struct { } `json:"result"` } -// Add structure for parsing the transaction -type BerachainTx struct { +// EVMChainTx represents the structure for parsing transactions +type EVMChainTx struct { MsgType uint32 DataLength uint32 Payload []byte @@ -196,8 +199,56 @@ var ( fileLogger *log.Logger ) +// Add this struct for HTTP client configuration +type httpClient struct { + client *http.Client + retries int +} + +// newHTTPClient creates an HTTP client with retry capabilities +func newHTTPClient() *httpClient { + return &httpClient{ + client: &http.Client{ + Timeout: 10 * time.Second, + }, + retries: 3, + } +} + +// doRequest executes an HTTP request with automatic retries on failure +func (c *httpClient) doRequest(req *http.Request) (*http.Response, error) { + var lastErr error + for i := 0; i <= c.retries; i++ { + resp, err := c.client.Do(req) + if err != nil { + lastErr = err + if i < c.retries { + time.Sleep(time.Second * time.Duration(1<= 500 && i < c.retries { + resp.Body.Close() + time.Sleep(time.Second * time.Duration(1<