2021-09-12 14:17:31 +00:00
|
|
|
package main
|
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
import (
|
2021-09-14 03:36:48 +00:00
|
|
|
"flag"
|
2021-09-13 03:25:47 +00:00
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2021-09-26 04:08:10 +00:00
|
|
|
"os"
|
2021-09-15 05:04:32 +00:00
|
|
|
"strconv"
|
2021-09-14 03:36:48 +00:00
|
|
|
"strings"
|
2021-09-26 03:38:16 +00:00
|
|
|
"sync"
|
2021-09-15 03:34:08 +00:00
|
|
|
"time"
|
2021-09-13 03:25:47 +00:00
|
|
|
|
|
|
|
"github.com/helium-blockchain-exporter/heliumapi"
|
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
)
|
|
|
|
|
|
|
|
// metricInfo is a metric exported by the helium blockchain exporter
|
|
|
|
type metricInfo struct {
|
|
|
|
Desc *prometheus.Desc
|
|
|
|
Type prometheus.ValueType
|
|
|
|
}
|
|
|
|
|
|
|
|
// Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics.
|
|
|
|
type Exporter struct {
|
2021-09-25 01:30:22 +00:00
|
|
|
Accounts []Account
|
|
|
|
StartTime time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewExporter returns an initialized Exporter
|
|
|
|
func NewExporter(accountAddress []string) (*Exporter, error) {
|
|
|
|
accounts := make([]Account, 0)
|
|
|
|
for _, accountAddress := range accountAddress {
|
|
|
|
if accountAddress != "" {
|
|
|
|
accounts = append(accounts, NewAccount(accountAddress))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &Exporter{
|
|
|
|
Accounts: accounts,
|
|
|
|
StartTime: time.Now(),
|
|
|
|
}, nil
|
2021-09-15 03:34:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Account represents a helium account
|
|
|
|
type Account struct {
|
2021-09-25 01:30:22 +00:00
|
|
|
Address string
|
|
|
|
Tx AccountTx
|
|
|
|
}
|
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
type AccountTx struct {
|
|
|
|
DepositTotal int
|
|
|
|
WithdrawalTotal int
|
|
|
|
LastUpdate time.Time
|
|
|
|
UpdateLock sync.Mutex
|
|
|
|
}
|
|
|
|
|
2021-09-25 01:30:22 +00:00
|
|
|
func NewAccount(address string) Account {
|
|
|
|
return Account{
|
|
|
|
Address: address,
|
|
|
|
Tx: AccountTx{
|
|
|
|
DepositTotal: 0,
|
|
|
|
WithdrawalTotal: 0,
|
|
|
|
LastUpdate: time.Now(),
|
|
|
|
},
|
|
|
|
}
|
2021-09-15 03:34:08 +00:00
|
|
|
}
|
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
const (
|
|
|
|
namespace = "helium"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2021-09-25 01:30:22 +00:00
|
|
|
// labels
|
2021-09-14 03:36:48 +00:00
|
|
|
commonAccountLabels = []string{"account"}
|
2021-09-14 03:51:47 +00:00
|
|
|
commonHotspotLabels = append(commonAccountLabels, "hotspot", "hotspot_name")
|
2021-09-14 03:36:48 +00:00
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
// exporter metrics
|
|
|
|
|
|
|
|
// helium oracle metrics
|
|
|
|
oraclePrice = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "oracle", "price_usd"),
|
|
|
|
"The oracle price of an HNT token in USD.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
// helium stats metrics
|
|
|
|
statsValidators = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "validators"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of validators.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsOuis = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "ouis"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of organization unique identifiers.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsHotspotsDataOnly = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of data only hotspots.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsBlocks = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "blocks"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total height/number of blocks in the blockchain.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsChallenges = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "challenges"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of challenges.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsCities = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "stats", "cities"),
|
|
|
|
"The number of cities with at least one helium hotspot.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
statsConsensusGroups = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "consensus_groups"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of consensus groups.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
statsCountries = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "stats", "countries"),
|
|
|
|
"The number of countries with at least on helium hotspot.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
statsHotspots = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "hotspots"),
|
2021-09-15 03:34:08 +00:00
|
|
|
"The total number of hotspots.",
|
2021-09-13 03:25:47 +00:00
|
|
|
nil, nil,
|
|
|
|
),
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.GaugeValue,
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
statsTokenSupply = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-25 01:30:22 +00:00
|
|
|
prometheus.BuildFQName(namespace, "stats", "token"),
|
2021-09-13 03:25:47 +00:00
|
|
|
"The total supply of HNT tokens in circulation.",
|
|
|
|
nil, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
// helium account metrics
|
|
|
|
accountBalanceHnt = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "account", "balance_hnt"),
|
2021-09-15 05:04:32 +00:00
|
|
|
"The number of HNT token owned by an account.",
|
2021-09-14 03:36:48 +00:00
|
|
|
commonAccountLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
accountActivity = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "account", "activity_total"),
|
2021-09-14 03:51:47 +00:00
|
|
|
"The total number of time an activity occurred in an account.",
|
2021-09-14 03:36:48 +00:00
|
|
|
append(commonAccountLabels, "type"), nil,
|
|
|
|
),
|
|
|
|
prometheus.CounterValue,
|
|
|
|
}
|
|
|
|
accountRewardsHnt = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.BuildFQName(namespace, "account", "rewards_hnt_total"),
|
2021-09-15 05:04:32 +00:00
|
|
|
"The number of HNT token rewarded to an account.",
|
2021-09-14 03:36:48 +00:00
|
|
|
commonAccountLabels, nil,
|
|
|
|
),
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.CounterValue,
|
2021-09-14 03:36:48 +00:00
|
|
|
}
|
2021-09-25 01:30:22 +00:00
|
|
|
accountDepositsHnt = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"),
|
|
|
|
"The number of HNT tokens deposited to this account.",
|
|
|
|
commonAccountLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.CounterValue,
|
|
|
|
}
|
|
|
|
accountWithdrawalsHnt = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"),
|
|
|
|
"The number of HNT tokens withdrawn from this account.",
|
|
|
|
commonAccountLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.CounterValue,
|
|
|
|
}
|
2021-09-13 03:25:47 +00:00
|
|
|
|
2021-09-15 05:04:32 +00:00
|
|
|
// helium hotspot metrics
|
|
|
|
hotspotUp = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "up"),
|
|
|
|
"Whether a hotspot is online.",
|
|
|
|
commonHotspotLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
2021-09-17 20:45:01 +00:00
|
|
|
hotspotRelayed = metricInfo{
|
2021-09-15 05:04:32 +00:00
|
|
|
prometheus.NewDesc(
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "relayed"),
|
|
|
|
"Whether a hotspot is relayed.",
|
2021-09-15 05:04:32 +00:00
|
|
|
commonHotspotLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
2021-09-17 20:45:01 +00:00
|
|
|
hotspotBlocks = metricInfo{
|
2021-09-15 05:04:32 +00:00
|
|
|
prometheus.NewDesc(
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "blocks_total"),
|
|
|
|
"The block height of a hotspot. Check on the hotspot itself for the most recent data.",
|
2021-09-15 05:04:32 +00:00
|
|
|
commonHotspotLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.CounterValue,
|
|
|
|
}
|
2021-09-17 20:45:01 +00:00
|
|
|
hotspotRewardsScale = metricInfo{
|
2021-09-15 05:04:32 +00:00
|
|
|
prometheus.NewDesc(
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "rewards_scale"),
|
2021-09-15 05:04:32 +00:00
|
|
|
"The reward scale of a hotspot.",
|
|
|
|
commonHotspotLabels, nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
hotspotGeocodeInfo = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "geocode_info"),
|
|
|
|
"Information on the location of a hotspot.",
|
|
|
|
append(commonHotspotLabels, "lng", "lat", "street", "state", "country", "city"), nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
hotspotAntennaInfo = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "antenna_info"),
|
2021-09-17 20:45:01 +00:00
|
|
|
"Information on the antenna of a hotspot.",
|
2021-09-15 05:04:32 +00:00
|
|
|
append(commonHotspotLabels, "gain", "elevation"), nil,
|
|
|
|
),
|
|
|
|
prometheus.GaugeValue,
|
|
|
|
}
|
|
|
|
hotspotActivity = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "activity_total"),
|
|
|
|
"The total number of time an activity occurred in a hotspot.",
|
|
|
|
append(commonHotspotLabels, "type"), nil,
|
|
|
|
),
|
|
|
|
prometheus.CounterValue,
|
|
|
|
}
|
|
|
|
hotspotRewardsHnt = metricInfo{
|
|
|
|
prometheus.NewDesc(
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.BuildFQName(namespace, "hotspot", "rewards_hnt_total"),
|
2021-09-15 05:04:32 +00:00
|
|
|
"The number of HNT token rewarded to a hotspot.",
|
|
|
|
commonHotspotLabels, nil,
|
|
|
|
),
|
2021-09-17 20:45:01 +00:00
|
|
|
prometheus.CounterValue,
|
2021-09-15 05:04:32 +00:00
|
|
|
}
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
|
2021-09-15 05:04:32 +00:00
|
|
|
func bool2Float64(b bool) float64 {
|
|
|
|
if b {
|
|
|
|
return 1.0
|
|
|
|
}
|
|
|
|
return 0.0
|
|
|
|
}
|
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
// Describe describes all the metrics ever exported by the helium blockchain exporter.
|
|
|
|
// implements prometheus.Collector.
|
|
|
|
func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
|
|
|
|
ch <- oraclePrice.Desc
|
|
|
|
|
|
|
|
ch <- statsValidators.Desc
|
|
|
|
ch <- statsOuis.Desc
|
|
|
|
ch <- statsHotspotsDataOnly.Desc
|
|
|
|
ch <- statsBlocks.Desc
|
|
|
|
ch <- statsChallenges.Desc
|
|
|
|
ch <- statsCities.Desc
|
|
|
|
ch <- statsConsensusGroups.Desc
|
|
|
|
ch <- statsCountries.Desc
|
|
|
|
ch <- statsHotspots.Desc
|
|
|
|
ch <- statsTokenSupply.Desc
|
2021-09-14 03:36:48 +00:00
|
|
|
|
|
|
|
ch <- accountBalanceHnt.Desc
|
|
|
|
ch <- accountActivity.Desc
|
|
|
|
ch <- accountRewardsHnt.Desc
|
2021-09-25 01:30:22 +00:00
|
|
|
ch <- accountDepositsHnt.Desc
|
|
|
|
ch <- accountWithdrawalsHnt.Desc
|
2021-09-15 05:04:32 +00:00
|
|
|
|
|
|
|
ch <- hotspotUp.Desc
|
2021-09-17 20:45:01 +00:00
|
|
|
ch <- hotspotRelayed.Desc
|
|
|
|
ch <- hotspotBlocks.Desc
|
|
|
|
ch <- hotspotRewardsScale.Desc
|
2021-09-15 05:04:32 +00:00
|
|
|
ch <- hotspotGeocodeInfo.Desc
|
|
|
|
ch <- hotspotAntennaInfo.Desc
|
|
|
|
ch <- hotspotActivity.Desc
|
|
|
|
ch <- hotspotRewardsHnt.Desc
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Collect fetches the data from the helium blockchain api and delivers them as Prometheus metrics.
|
|
|
|
// implements prometheus.Collector.
|
|
|
|
func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
|
2021-09-26 03:38:16 +00:00
|
|
|
wg := new(sync.WaitGroup)
|
|
|
|
|
|
|
|
wg.Add(2)
|
|
|
|
go e.collectOracleMetrics(wg, ch)
|
2021-09-26 16:33:19 +00:00
|
|
|
go e.collectStatsMetrics(wg, ch)
|
2021-09-17 20:45:01 +00:00
|
|
|
for i := range e.Accounts {
|
2021-09-26 03:38:16 +00:00
|
|
|
wg.Add(5)
|
|
|
|
go e.collectAccountMetrics(wg, ch, &e.Accounts[i])
|
|
|
|
go e.collectAccountActivityMetrics(wg, ch, &e.Accounts[i])
|
|
|
|
go e.collectAccountRewardsTotalMetrics(wg, ch, &e.Accounts[i])
|
|
|
|
go e.collectAccountTransactionsMetrics(wg, ch, &e.Accounts[i])
|
|
|
|
|
|
|
|
go e.collectHotspotMetrics(wg, ch, &e.Accounts[i])
|
2021-09-14 03:36:48 +00:00
|
|
|
}
|
2021-09-26 03:38:16 +00:00
|
|
|
wg.Wait()
|
2021-09-13 03:25:47 +00:00
|
|
|
}
|
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
// collectOracleMetrics collect metrics in the oracle group from the helium api
|
2021-09-26 03:38:16 +00:00
|
|
|
func (e *Exporter) collectOracleMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
currentOraclePrice, err := heliumapi.GetCurrentOraclePrice()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
oraclePrice.Desc, oraclePrice.Type, float64(currentOraclePrice.Price)/100000000,
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
// collectStatsMetrics collect metrics in the stats group from the helium api
|
2021-09-26 03:38:16 +00:00
|
|
|
func (e *Exporter) collectStatsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2021-09-13 03:25:47 +00:00
|
|
|
blockchainStats, err := heliumapi.GetBlockchainStats()
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsValidators.Desc, statsValidators.Type, float64(blockchainStats.Counts.Validators),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsOuis.Desc, statsOuis.Type, float64(blockchainStats.Counts.Ouis),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsHotspotsDataOnly.Desc, statsHotspotsDataOnly.Type, float64(blockchainStats.Counts.HotspotsDataonly),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsBlocks.Desc, statsBlocks.Type, float64(blockchainStats.Counts.Blocks),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsChallenges.Desc, statsChallenges.Type, float64(blockchainStats.Counts.Challenges),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsCities.Desc, statsCities.Type, float64(blockchainStats.Counts.Cities),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsConsensusGroups.Desc, statsConsensusGroups.Type, float64(blockchainStats.Counts.ConsensusGroups),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsCountries.Desc, statsCountries.Type, float64(blockchainStats.Counts.Countries),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsHotspots.Desc, statsHotspots.Type, float64(blockchainStats.Counts.Hotspots),
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
statsTokenSupply.Desc, statsTokenSupply.Type, blockchainStats.TokenSupply,
|
2021-09-13 03:25:47 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
// collectStatsMetrics collect metrics in the account group from the helium api
|
2021-09-26 03:38:16 +00:00
|
|
|
func (e *Exporter) collectAccountMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2021-09-25 01:30:22 +00:00
|
|
|
accountForAddress, err := heliumapi.GetAccountForAddress(account.Address)
|
2021-09-15 03:34:08 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Balance),
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Address,
|
|
|
|
)
|
|
|
|
}
|
2021-09-15 03:34:08 +00:00
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
// collectAccountActivityMetrics collect the total number of activities executed by an account from the helium api
|
|
|
|
func (e *Exporter) collectAccountActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.Address)
|
2021-09-14 03:51:47 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-26 03:53:20 +00:00
|
|
|
for accType, count := range *accountActivityForAddress {
|
2021-09-15 03:34:08 +00:00
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
accountActivity.Desc, accountActivity.Type, float64(count),
|
2021-09-25 01:30:22 +00:00
|
|
|
account.Address, accType,
|
2021-09-15 03:34:08 +00:00
|
|
|
)
|
|
|
|
}
|
2021-09-14 03:36:48 +00:00
|
|
|
}
|
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
// collectAccountRewardsTotalMetrics collect the total rewards accumulated by an account from the helium api
|
|
|
|
func (e *Exporter) collectAccountRewardsTotalMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
accountRewardTotalsForAddress, err := heliumapi.GetRewardTotalsForAccount(account.Address, &e.StartTime, nil)
|
2021-09-15 05:04:32 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Sum,
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Address,
|
|
|
|
)
|
|
|
|
}
|
2021-09-17 20:45:01 +00:00
|
|
|
|
2021-09-26 03:38:16 +00:00
|
|
|
// collectAccountTransactionsMetrics collect the total deposited/withdrawn by an account from the helium api
|
|
|
|
func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
|
|
|
|
defer wg.Done()
|
2021-09-17 20:45:01 +00:00
|
|
|
|
2021-09-26 16:33:19 +00:00
|
|
|
// we can only want to allow a single instance of the routine doing
|
2021-09-26 03:38:16 +00:00
|
|
|
// calculations on the deposited and widthdrawn total
|
|
|
|
account.Tx.UpdateLock.Lock()
|
|
|
|
defer account.Tx.UpdateLock.Unlock()
|
2021-09-15 05:04:32 +00:00
|
|
|
|
2021-09-25 01:30:22 +00:00
|
|
|
now := time.Now()
|
2021-09-26 03:38:16 +00:00
|
|
|
activities, err := heliumapi.GetActivityForAccount(account.Address, []string{}, &account.Tx.LastUpdate, &now)
|
2021-09-25 01:30:22 +00:00
|
|
|
if err != nil {
|
2021-09-26 03:38:16 +00:00
|
|
|
fmt.Println(err)
|
|
|
|
return
|
2021-09-15 03:34:08 +00:00
|
|
|
}
|
2021-09-12 14:17:31 +00:00
|
|
|
|
2021-09-26 03:53:20 +00:00
|
|
|
// impl based on https://github.com/helium/hotspot-app/blob/918563fba84d1abf4554a43a4d42bb838d017bd3/src/features/wallet/root/useActivityItem.tsx#L336
|
2021-09-25 19:09:40 +00:00
|
|
|
for _, activity := range activities.AddGatewayV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.StakingFee
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
for _, activity := range activities.AssertLocationV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.StakingFee
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
for _, activity := range activities.AssertLocationV2 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.StakingFee
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
for _, activity := range activities.PaymentV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
if activity.Payer == account.Address {
|
|
|
|
account.Tx.WithdrawalTotal += activity.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
} else {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.DepositTotal += activity.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, activity := range activities.PaymentV2 {
|
2021-09-26 03:38:16 +00:00
|
|
|
if activity.Payer == account.Address {
|
2021-09-25 19:09:40 +00:00
|
|
|
paymentTotal := 0
|
|
|
|
for _, payment := range activity.Payments {
|
|
|
|
paymentTotal += payment.Amount
|
|
|
|
}
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += paymentTotal
|
2021-09-25 19:09:40 +00:00
|
|
|
} else {
|
|
|
|
for _, payment := range activity.Payments {
|
2021-09-26 03:38:16 +00:00
|
|
|
if payment.Payee == account.Address {
|
|
|
|
account.Tx.DepositTotal += payment.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, activity := range activities.RewardsV1 {
|
|
|
|
for _, reward := range activity.Rewards {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.DepositTotal += reward.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, activity := range activities.RewardsV2 {
|
|
|
|
for _, reward := range activity.Rewards {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.DepositTotal += reward.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, activity := range activities.StakeValidatorV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.Stake
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
for _, activity := range activities.TokenBurnV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.Amount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
for _, activity := range activities.TransferHotspotV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
if activity.Buyer == account.Address {
|
|
|
|
account.Tx.WithdrawalTotal += activity.AmountToSeller
|
2021-09-25 19:09:40 +00:00
|
|
|
} else {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.DepositTotal += activity.AmountToSeller
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, activity := range activities.UnstakeValidatorV1 {
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.WithdrawalTotal += activity.StakeAmount
|
2021-09-25 19:09:40 +00:00
|
|
|
}
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Tx.LastUpdate = now
|
2021-09-25 19:09:40 +00:00
|
|
|
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:38:16 +00:00
|
|
|
accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(account.Tx.DepositTotal),
|
|
|
|
account.Address,
|
2021-09-25 19:09:40 +00:00
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:38:16 +00:00
|
|
|
accountWithdrawalsHnt.Desc, accountWithdrawalsHnt.Type, float64(account.Tx.WithdrawalTotal),
|
|
|
|
account.Address,
|
2021-09-25 19:09:40 +00:00
|
|
|
)
|
2021-09-26 03:38:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// collectStatsMetrics collect metrics of the hotspot of an account from the helium api
|
|
|
|
func (e *Exporter) collectHotspotMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
hotspotsForAddress, err := heliumapi.GetHotspotsForAccount(account.Address)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
2021-09-25 19:09:40 +00:00
|
|
|
|
2021-09-26 03:53:20 +00:00
|
|
|
for _, hotspotData := range *hotspotsForAddress {
|
2021-09-26 03:38:16 +00:00
|
|
|
// collect hotspot metric requiring extra queries in a new routine
|
|
|
|
wg.Add(2)
|
|
|
|
go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData)
|
|
|
|
go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData)
|
|
|
|
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"),
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name,
|
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotRelayed.Desc, hotspotRelayed.Type, bool2Float64(len(hotspotData.Status.ListenAddrs) > 0 && strings.HasPrefix(hotspotData.Status.ListenAddrs[0], "/p2p")),
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name,
|
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotBlocks.Desc, hotspotBlocks.Type, float64(hotspotData.Status.Height),
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name,
|
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotRewardsScale.Desc, hotspotRewardsScale.Type, float64(hotspotData.RewardScale),
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name,
|
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotGeocodeInfo.Desc, hotspotGeocodeInfo.Type, 1.0,
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name, strconv.FormatFloat(hotspotData.Lng, 'f', 6, 64), strconv.FormatFloat(hotspotData.Lat, 'f', 6, 64), hotspotData.Geocode.LongStreet, hotspotData.Geocode.LongState, hotspotData.Geocode.LongCountry, hotspotData.Geocode.LongCity,
|
|
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0,
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name, strconv.Itoa(hotspotData.Gain), strconv.Itoa(hotspotData.Elevation),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// collectHotspotActivityMetrics collect the total number of activities executed by a hotspot from the helium api
|
|
|
|
func (e *Exporter) collectHotspotActivityMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
2021-09-26 04:42:34 +00:00
|
|
|
hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCounts(hotspotData.Address)
|
2021-09-26 03:38:16 +00:00
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-26 03:53:20 +00:00
|
|
|
for accType, count := range *hotspotActivityForAddress {
|
2021-09-26 03:38:16 +00:00
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
|
|
hotspotActivity.Desc, hotspotActivity.Type, float64(count),
|
|
|
|
account.Address, hotspotData.Address, hotspotData.Name, accType,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api
|
|
|
|
func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) {
|
|
|
|
defer wg.Done()
|
|
|
|
|
|
|
|
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
2021-09-26 03:53:20 +00:00
|
|
|
hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Sum,
|
2021-09-26 03:38:16 +00:00
|
|
|
account.Address, hotspotData.Address, hotspotData.Name,
|
|
|
|
)
|
2021-09-15 03:34:08 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 14:17:31 +00:00
|
|
|
func main() {
|
2021-09-26 04:08:10 +00:00
|
|
|
fApiUrl := flag.String("apiUrl", "https://api.helium.io", "The helium api url")
|
2021-09-25 19:09:40 +00:00
|
|
|
fHeliumAccounts := flag.String("accounts", "", "A comma-delimited list of helium accounts to scrape (optional)")
|
2021-09-14 03:36:48 +00:00
|
|
|
fMetricsPath := flag.String("metricpath", "/metrics", "The metrics path")
|
|
|
|
fListenAddress := flag.String("listenAddress", "0.0.0.0", "The http server listen address")
|
2021-09-25 19:09:40 +00:00
|
|
|
fListenPort := flag.String("listenPort", "9865", "The http server listen port")
|
2021-09-14 03:36:48 +00:00
|
|
|
flag.Parse()
|
|
|
|
|
2021-09-26 04:08:10 +00:00
|
|
|
heliumapi.ApiUrl = *fApiUrl
|
2021-09-15 03:34:08 +00:00
|
|
|
heliumAccounts := strings.Split(*fHeliumAccounts, ",")
|
2021-09-14 03:36:48 +00:00
|
|
|
serverAddr := *fListenAddress + ":" + *fListenPort
|
2021-09-13 03:25:47 +00:00
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
e, err := NewExporter(heliumAccounts)
|
2021-09-13 03:25:47 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("failed to start exporter: %s", err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
r := prometheus.NewRegistry()
|
|
|
|
r.MustRegister(e)
|
|
|
|
|
|
|
|
// setup http route
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
_, _ = w.Write([]byte(`<html>
|
|
|
|
<head><title>Helium blockchain exporter</title></head>
|
|
|
|
<body>
|
|
|
|
<h1>Helium blockchain exporter</h1>
|
2021-09-14 03:36:48 +00:00
|
|
|
<p><a href='` + *fMetricsPath + `'>Metrics</a></p>
|
2021-09-13 03:25:47 +00:00
|
|
|
</body>
|
|
|
|
</html>`))
|
|
|
|
})
|
|
|
|
|
2021-09-14 03:36:48 +00:00
|
|
|
http.Handle(*fMetricsPath, promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
|
2021-09-15 03:34:08 +00:00
|
|
|
fmt.Printf("listening on %v\n", serverAddr)
|
2021-09-26 04:08:10 +00:00
|
|
|
if err = http.ListenAndServe(serverAddr, nil); err != nil {
|
|
|
|
fmt.Println(err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2021-09-12 14:17:31 +00:00
|
|
|
}
|