Skip to content

Commit

Permalink
feat: allow configurable headers on execution node
Browse files Browse the repository at this point in the history
  • Loading branch information
Savid committed Sep 14, 2022
1 parent 0e9a046 commit e434196
Show file tree
Hide file tree
Showing 14 changed files with 218 additions and 71 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Ethereum Address Metrics Exporter relies entirely on a single `yaml` config file
| global.namespace | `eth_address` | The prefix added to every metric |
| global.labels[] | | Key value pair of labels to add to every metric |
| execution.url | `http://localhost:8545` | URL to the execution node |
| execution.timeout | `10s` | Timeout for requests to the execution node |
| execution.headers[] | | Key value pair of headers to add on every request |
| addresses.eoa | | List of ethereum externally owned account addresses |
| addresses.eoa[].name | | Name of the address, will be a label on the metric |
| addresses.eoa[].address | | Ethereum externally owned account address |
Expand Down Expand Up @@ -74,6 +76,9 @@ global:

execution:
url: "http://localhost:8545"
timeout: 10s
headers:
authorization: "Basic abc123"

addresses:
eoa:
Expand Down
3 changes: 3 additions & 0 deletions example_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ global:

execution:
url: "http://localhost:8545"
timeout: 10s
headers:
authorization: "Basic abc123"

addresses:
eoa:
Expand Down
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ go 1.17

require (
github.com/creasty/defaults v1.6.0
github.com/onrik/ethrpc v1.0.0
github.com/prometheus/client_golang v1.12.1
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.4.0
Expand All @@ -18,16 +17,15 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jarcoal/httpmock v1.2.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stretchr/testify v1.7.5 // indirect
github.com/tidwall/gjson v1.14.3 // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df // indirect
google.golang.org/protobuf v1.26.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)
18 changes: 4 additions & 14 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jarcoal/httpmock v1.2.0 h1:gSvTxxFR/MEMfsGrvRbdfpRUMBStovlSRLw0Ep1bwwc=
github.com/jarcoal/httpmock v1.2.0/go.mod h1:oCoTsnAz4+UoOUIf5lJOWV2QQIW5UoeUI6aM2YnWAZk=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
Expand All @@ -155,16 +153,13 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/maxatome/go-testdeep v1.11.0/go.mod h1:011SgQ6efzZYAen6fDn4BqQ+lUR72ysdyKe7Dyogw70=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/onrik/ethrpc v1.0.0 h1:caG0U/Y1op32mntqmDnwvzue4Tg37cSA2EU8PMIMSjM=
github.com/onrik/ethrpc v1.0.0/go.mod h1:RoqOlDiBBs1qYamkcYhxMgkPijxu5R8t55mgUiy4le8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -212,12 +207,6 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.5 h1:s5PTfem8p8EbKQOctVV53k6jCJt3UX4IEJzwh+C324Q=
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -343,8 +332,8 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down Expand Up @@ -398,8 +387,9 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df h1:5Pf6pFKu98ODmgnpvkJ3kFUOQGGLIzLIkbzUHp47618=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
Expand Down
143 changes: 143 additions & 0 deletions pkg/exporter/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package api

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"github.com/sirupsen/logrus"
)

// ExecutionClient is an interface for executing RPC calls to the Ethereum node.
type ExecutionClient interface {
// ETHCall executes a new message call immediately without creating a transaction on the block chain.
ETHCall(transaction *ETHCallTransaction, block string) (string, error)
// ETHGetBalance returns the balance of the account of given address.
ETHGetBalance(address string, block string) (string, error)
}

type ETHCallTransaction struct {
From *string `json:"from"`
To string `json:"to"`
Gas *string `json:"gas"`
GasPrice *string `json:"gasPrice"`
Value *string `json:"value"`
Data *string `json:"data"`
}

type executionClient struct {
url string
log logrus.FieldLogger
client http.Client
headers map[string]string
}

// NewExecutionClient creates a new ExecutionClient.
func NewExecutionClient(log logrus.FieldLogger, url string, headers map[string]string, timeout time.Duration) ExecutionClient {
client := http.Client{
Timeout: timeout,
}

return &executionClient{
url: url,
log: log,
client: client,
headers: headers,
}
}

type apiResponse struct {
JSONRpc string `json:"jsonrpc"`
ID int64 `json:"id"`
Result json.RawMessage `json:"result"`
}

//nolint:unparam // ctx will probably be used in the future
func (e *executionClient) post(method string, params interface{}, id int) (json.RawMessage, error) {
body := map[string]interface{}{
"jsonrpc": "2.0",
"method": method,
"id": id,
"params": params,
}

jsonData, err := json.Marshal(body)
if err != nil {
return nil, err
}

req, err := http.NewRequest("POST", e.url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}

for k, v := range e.headers {
req.Header.Set(k, v)
}

req.Header.Set("Content-Type", "application/json")

rsp, err := e.client.Do(req)
if err != nil {
return nil, err
}

defer rsp.Body.Close()

if rsp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("status code: %d", rsp.StatusCode)
}

data, err := io.ReadAll(rsp.Body)
if err != nil {
return nil, err
}

resp := new(apiResponse)
if err := json.Unmarshal(data, resp); err != nil {
return nil, err
}

return resp.Result, nil
}

func (e *executionClient) ETHCall(transaction *ETHCallTransaction, block string) (string, error) {
params := []interface{}{
transaction,
block,
}

rsp, err := e.post("eth_call", params, 1)
if err != nil {
return "", err
}

ethCall := ""
if err := json.Unmarshal(rsp, &ethCall); err != nil {
return "", err
}

return ethCall, nil
}

func (e *executionClient) ETHGetBalance(address, block string) (string, error) {
params := []interface{}{
address,
block,
}

rsp, err := e.post("eth_getBalance", params, 1)
if err != nil {
return "", err
}

ethGetBalance := ""
if err := json.Unmarshal(rsp, &ethGetBalance); err != nil {
return "", err
}

return ethGetBalance, nil
}
5 changes: 4 additions & 1 deletion pkg/exporter/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package exporter

import (
"fmt"
"time"

"github.com/savid/ethereum-address-metrics-exporter/pkg/exporter/jobs"
)
Expand All @@ -24,7 +25,9 @@ type GlobalConfig struct {

// ExecutionNode represents a single ethereum execution client.
type ExecutionNode struct {
URL string `yaml:"url" default:"http://localhost:8545"`
URL string `yaml:"url" default:"http://localhost:8545"`
Headers map[string]string `yaml:"headers"`
Timeout time.Duration `yaml:"timeout" default:"10s"`
}

type Addresses struct {
Expand Down
10 changes: 5 additions & 5 deletions pkg/exporter/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"net/http"
"time"

"github.com/onrik/ethrpc"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/savid/ethereum-address-metrics-exporter/pkg/exporter/api"
"github.com/sirupsen/logrus"
)

Expand All @@ -33,21 +33,21 @@ type exporter struct {
// Helpers
log logrus.FieldLogger
Cfg *Config
// Exporters
execution *ethrpc.EthRPC

execution api.ExecutionClient
// Metrics
metrics Metrics
}

func (e *exporter) Start(ctx context.Context) error {
e.log.Info("Initializing...")

e.execution = ethrpc.New(e.Cfg.Execution.URL)
e.execution = api.NewExecutionClient(e.log, e.Cfg.Execution.URL, e.Cfg.Execution.Headers, e.Cfg.Execution.Timeout)

e.metrics = NewMetrics(e.execution, e.log, e.Cfg.GlobalConfig.Namespace, e.Cfg.GlobalConfig.Labels, &e.Cfg.Addresses)

e.log.
WithField("execution_url", e.execution.URL()).
WithField("execution_url", e.Cfg.Execution.URL).
Info(fmt.Sprintf("Starting metrics server on %v", e.Cfg.GlobalConfig.MetricsAddr))

http.Handle("/metrics", promhttp.Handler())
Expand Down
15 changes: 8 additions & 7 deletions pkg/exporter/jobs/chainlink_data_feed.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import (
"context"
"time"

"github.com/onrik/ethrpc"
"github.com/prometheus/client_golang/prometheus"
"github.com/savid/ethereum-address-metrics-exporter/pkg/exporter/api"
"github.com/sirupsen/logrus"
)

// ChainlinkDataFeed exposes metrics for ethereum chainlink data feed contract
type ChainlinkDataFeed struct {
client *ethrpc.EthRPC
client api.ExecutionClient
log logrus.FieldLogger
ChainlinkDataFeedBalance prometheus.GaugeVec
addresses []*AddressChainlinkDataFeed
Expand All @@ -33,7 +33,7 @@ func (n *ChainlinkDataFeed) Name() string {
}

// NewChainlinkDataFeed returns a new ChainlinkDataFeed instance.
func NewChainlinkDataFeed(client *ethrpc.EthRPC, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressChainlinkDataFeed) ChainlinkDataFeed {
func NewChainlinkDataFeed(client api.ExecutionClient, log logrus.FieldLogger, namespace string, constLabels map[string]string, addresses []*AddressChainlinkDataFeed) ChainlinkDataFeed {
namespace += "_" + NameChainlinkDataFeed

instance := ChainlinkDataFeed{
Expand Down Expand Up @@ -81,11 +81,12 @@ func (n *ChainlinkDataFeed) tick(ctx context.Context) {
}

func (n *ChainlinkDataFeed) getBalance(address *AddressChainlinkDataFeed) error {
balanceStr, err := n.client.EthCall(ethrpc.T{
// call latestAnswer() which is 0x50d25bcd
latestAnswerData := "0x50d25bcd000000000000000000000000"

balanceStr, err := n.client.ETHCall(&api.ETHCallTransaction{
To: address.Contract,
From: "0x0000000000000000000000000000000000000000",
// call latestAnswer() which is 0x50d25bcd
Data: "0x50d25bcd000000000000000000000000",
Data: &latestAnswerData,
}, "latest")
if err != nil {
return err
Expand Down
Loading

0 comments on commit e434196

Please sign in to comment.