1
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
Massaki Archambault 42e2a23e26 add option to supplu api url
continuous-integration/drone/push Build is failing Details
2021-09-26 00:08:10 -04:00
Massaki Archambault 592fbea0e6 refactor make api function pass only the data object instead of the whole response 2021-09-25 23:53:20 -04:00
Massaki Archambault 20eddb8fc3 make each new query to the helium api be in its own goroutine 2021-09-25 23:38:16 -04:00
8 changed files with 358 additions and 294 deletions

View File

@ -5,8 +5,10 @@ import (
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/helium-blockchain-exporter/heliumapi" "github.com/helium-blockchain-exporter/heliumapi"
@ -46,6 +48,13 @@ type Account struct {
Tx AccountTx Tx AccountTx
} }
type AccountTx struct {
DepositTotal int
WithdrawalTotal int
LastUpdate time.Time
UpdateLock sync.Mutex
}
func NewAccount(address string) Account { func NewAccount(address string) Account {
return Account{ return Account{
Address: address, Address: address,
@ -57,12 +66,6 @@ func NewAccount(address string) Account {
} }
} }
type AccountTx struct {
DepositTotal int
WithdrawalTotal int
LastUpdate time.Time
}
const ( const (
namespace = "helium" namespace = "helium"
) )
@ -317,16 +320,27 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
// Collect fetches the data from the helium blockchain api and delivers them as Prometheus metrics. // Collect fetches the data from the helium blockchain api and delivers them as Prometheus metrics.
// implements prometheus.Collector. // implements prometheus.Collector.
func (e *Exporter) Collect(ch chan<- prometheus.Metric) { func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
e.collectOracleMetrics(ch) wg := new(sync.WaitGroup)
e.collectStatsMetrics(ch)
wg.Add(2)
go e.collectOracleMetrics(wg, ch)
e.collectStatsMetrics(wg, ch)
for i := range e.Accounts { for i := range e.Accounts {
e.collectAccountMetrics(ch, &e.Accounts[i]) wg.Add(5)
e.collectHotspotMetrics(ch, &e.Accounts[i]) 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])
} }
wg.Wait()
} }
// collectOracleMetrics collect metrics in the oracle group from the helium api // collectOracleMetrics collect metrics in the oracle group from the helium api
func (e *Exporter) collectOracleMetrics(ch chan<- prometheus.Metric) { func (e *Exporter) collectOracleMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric) {
defer wg.Done()
currentOraclePrice, err := heliumapi.GetCurrentOraclePrice() currentOraclePrice, err := heliumapi.GetCurrentOraclePrice()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -334,12 +348,14 @@ func (e *Exporter) collectOracleMetrics(ch chan<- prometheus.Metric) {
} }
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
oraclePrice.Desc, oraclePrice.Type, float64(currentOraclePrice.Data.Price)/100000000, oraclePrice.Desc, oraclePrice.Type, float64(currentOraclePrice.Price)/100000000,
) )
} }
// collectStatsMetrics collect metrics in the stats group from the helium api // collectStatsMetrics collect metrics in the stats group from the helium api
func (e *Exporter) collectStatsMetrics(ch chan<- prometheus.Metric) { func (e *Exporter) collectStatsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric) {
defer wg.Done()
blockchainStats, err := heliumapi.GetBlockchainStats() blockchainStats, err := heliumapi.GetBlockchainStats()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
@ -347,98 +363,188 @@ func (e *Exporter) collectStatsMetrics(ch chan<- prometheus.Metric) {
} }
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsValidators.Desc, statsValidators.Type, float64(blockchainStats.Data.Counts.Validators), statsValidators.Desc, statsValidators.Type, float64(blockchainStats.Counts.Validators),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsOuis.Desc, statsOuis.Type, float64(blockchainStats.Data.Counts.Ouis), statsOuis.Desc, statsOuis.Type, float64(blockchainStats.Counts.Ouis),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsHotspotsDataOnly.Desc, statsHotspotsDataOnly.Type, float64(blockchainStats.Data.Counts.HotspotsDataonly), statsHotspotsDataOnly.Desc, statsHotspotsDataOnly.Type, float64(blockchainStats.Counts.HotspotsDataonly),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsBlocks.Desc, statsBlocks.Type, float64(blockchainStats.Data.Counts.Blocks), statsBlocks.Desc, statsBlocks.Type, float64(blockchainStats.Counts.Blocks),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsChallenges.Desc, statsChallenges.Type, float64(blockchainStats.Data.Counts.Challenges), statsChallenges.Desc, statsChallenges.Type, float64(blockchainStats.Counts.Challenges),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsCities.Desc, statsCities.Type, float64(blockchainStats.Data.Counts.Cities), statsCities.Desc, statsCities.Type, float64(blockchainStats.Counts.Cities),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsConsensusGroups.Desc, statsConsensusGroups.Type, float64(blockchainStats.Data.Counts.ConsensusGroups), statsConsensusGroups.Desc, statsConsensusGroups.Type, float64(blockchainStats.Counts.ConsensusGroups),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsCountries.Desc, statsCountries.Type, float64(blockchainStats.Data.Counts.Countries), statsCountries.Desc, statsCountries.Type, float64(blockchainStats.Counts.Countries),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsHotspots.Desc, statsHotspots.Type, float64(blockchainStats.Data.Counts.Hotspots), statsHotspots.Desc, statsHotspots.Type, float64(blockchainStats.Counts.Hotspots),
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
statsTokenSupply.Desc, statsTokenSupply.Type, blockchainStats.Data.TokenSupply, statsTokenSupply.Desc, statsTokenSupply.Type, blockchainStats.TokenSupply,
) )
} }
// collectStatsMetrics collect metrics in the account group from the helium api // collectStatsMetrics collect metrics in the account group from the helium api
func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account *Account) { func (e *Exporter) collectAccountMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
defer wg.Done()
accountForAddress, err := heliumapi.GetAccountForAddress(account.Address) accountForAddress, err := heliumapi.GetAccountForAddress(account.Address)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
ch <- prometheus.MustNewConstMetric(
accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Balance),
account.Address,
)
}
// 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) accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.Address)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
accountRewardTotalsForAddress, err := heliumapi.GetRewardTotalsForAccount(account.Address, &e.StartTime, nil) for accType, count := range *accountActivityForAddress {
if err != nil {
fmt.Println(err)
return
}
err = account.collectTransactionMetrics(ch)
if err != nil {
fmt.Println(err)
return
}
ch <- prometheus.MustNewConstMetric(
accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Data.Balance),
account.Address,
)
for accType, count := range accountActivityForAddress.Data {
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountActivity.Desc, accountActivity.Type, float64(count), accountActivity.Desc, accountActivity.Type, float64(count),
account.Address, accType, account.Address, accType,
) )
} }
}
// 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)
if err != nil {
fmt.Println(err)
return
}
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Data.Sum, accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Sum,
account.Address, account.Address,
) )
} }
// collectStatsMetrics collect metrics in the hotspot group from the helium api // collectAccountTransactionsMetrics collect the total deposited/withdrawn by an account from the helium api
func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *Account) { func (e *Exporter) collectAccountTransactionsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) {
defer wg.Done()
// we can only ever allow a single instance of the routine doing
// calculations on the deposited and widthdrawn total
account.Tx.UpdateLock.Lock()
defer account.Tx.UpdateLock.Unlock()
now := time.Now()
activities, err := heliumapi.GetActivityForAccount(account.Address, []string{}, &account.Tx.LastUpdate, &now)
if err != nil {
fmt.Println(err)
return
}
// impl based on https://github.com/helium/hotspot-app/blob/918563fba84d1abf4554a43a4d42bb838d017bd3/src/features/wallet/root/useActivityItem.tsx#L336
for _, activity := range activities.AddGatewayV1 {
account.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.AssertLocationV1 {
account.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.AssertLocationV2 {
account.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.PaymentV1 {
if activity.Payer == account.Address {
account.Tx.WithdrawalTotal += activity.Amount
} else {
account.Tx.DepositTotal += activity.Amount
}
}
for _, activity := range activities.PaymentV2 {
if activity.Payer == account.Address {
paymentTotal := 0
for _, payment := range activity.Payments {
paymentTotal += payment.Amount
}
account.Tx.WithdrawalTotal += paymentTotal
} else {
for _, payment := range activity.Payments {
if payment.Payee == account.Address {
account.Tx.DepositTotal += payment.Amount
}
}
}
}
for _, activity := range activities.RewardsV1 {
for _, reward := range activity.Rewards {
account.Tx.DepositTotal += reward.Amount
}
}
for _, activity := range activities.RewardsV2 {
for _, reward := range activity.Rewards {
account.Tx.DepositTotal += reward.Amount
}
}
for _, activity := range activities.StakeValidatorV1 {
account.Tx.WithdrawalTotal += activity.Stake
}
for _, activity := range activities.TokenBurnV1 {
account.Tx.WithdrawalTotal += activity.Amount
}
for _, activity := range activities.TransferHotspotV1 {
if activity.Buyer == account.Address {
account.Tx.WithdrawalTotal += activity.AmountToSeller
} else {
account.Tx.DepositTotal += activity.AmountToSeller
}
}
for _, activity := range activities.UnstakeValidatorV1 {
account.Tx.WithdrawalTotal += activity.StakeAmount
}
account.Tx.LastUpdate = now
ch <- prometheus.MustNewConstMetric(
accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(account.Tx.DepositTotal),
account.Address,
)
ch <- prometheus.MustNewConstMetric(
accountWithdrawalsHnt.Desc, accountWithdrawalsHnt.Type, float64(account.Tx.WithdrawalTotal),
account.Address,
)
}
// 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) hotspotsForAddress, err := heliumapi.GetHotspotsForAccount(account.Address)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
for _, hotspotData := range hotspotsForAddress.Data { for _, hotspotData := range *hotspotsForAddress {
hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCount(hotspotData.Address) // collect hotspot metric requiring extra queries in a new routine
if err != nil { wg.Add(2)
fmt.Println(err) go e.collectHotspotActivityMetrics(wg, ch, account, hotspotData)
return go e.collectHotspotRewardsMetrics(wg, ch, account, hotspotData)
}
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)
if err != nil {
fmt.Println(err)
return
}
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"), hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"),
@ -464,105 +570,52 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A
hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0, hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0,
account.Address, hotspotData.Address, hotspotData.Name, strconv.Itoa(hotspotData.Gain), strconv.Itoa(hotspotData.Elevation), account.Address, hotspotData.Address, hotspotData.Name, strconv.Itoa(hotspotData.Gain), strconv.Itoa(hotspotData.Elevation),
) )
for accType, count := range hotspotActivityForAddress.Data { }
ch <- prometheus.MustNewConstMetric( }
hotspotActivity.Desc, hotspotActivity.Type, float64(count),
account.Address, hotspotData.Address, hotspotData.Name, accType, // 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()
hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCount(hotspotData.Address)
if err != nil {
fmt.Println(err)
return
}
for accType, count := range *hotspotActivityForAddress {
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Data.Sum, hotspotActivity.Desc, hotspotActivity.Type, float64(count),
account.Address, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name, accType,
) )
} }
} }
func (a *Account) collectTransactionMetrics(ch chan<- prometheus.Metric) error { // collectHotspotRewardsMetrics collect the total rewards accumulated by a hotspot from the helium api
now := time.Now() func (e *Exporter) collectHotspotRewardsMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account, hotspotData heliumapi.AccountHotspot) {
activities, err := heliumapi.GetActivityForAccount(a.Address, []string{}, &a.Tx.LastUpdate, &now) defer wg.Done()
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)
if err != nil { if err != nil {
return err fmt.Println(err)
return
} }
// logic based on https://github.com/helium/hotspot-app/blob/918563fba84d1abf4554a43a4d42bb838d017bd3/src/features/wallet/root/useActivityItem.tsx#L336
for _, activity := range activities.AddGatewayV1 {
a.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.AssertLocationV1 {
a.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.AssertLocationV2 {
a.Tx.WithdrawalTotal += activity.StakingFee
}
for _, activity := range activities.PaymentV1 {
if activity.Payer == a.Address {
a.Tx.WithdrawalTotal += activity.Amount
} else {
a.Tx.DepositTotal += activity.Amount
}
}
for _, activity := range activities.PaymentV2 {
if activity.Payer == a.Address {
paymentTotal := 0
for _, payment := range activity.Payments {
paymentTotal += payment.Amount
}
a.Tx.WithdrawalTotal += paymentTotal
} else {
for _, payment := range activity.Payments {
if payment.Payee == a.Address {
a.Tx.DepositTotal += payment.Amount
}
}
}
}
for _, activity := range activities.RewardsV1 {
for _, reward := range activity.Rewards {
a.Tx.DepositTotal += reward.Amount
}
}
for _, activity := range activities.RewardsV2 {
for _, reward := range activity.Rewards {
a.Tx.DepositTotal += reward.Amount
}
}
for _, activity := range activities.StakeValidatorV1 {
a.Tx.WithdrawalTotal += activity.Stake
}
for _, activity := range activities.TokenBurnV1 {
a.Tx.WithdrawalTotal += activity.Amount
}
for _, activity := range activities.TransferHotspotV1 {
if activity.Buyer == a.Address {
a.Tx.WithdrawalTotal += activity.AmountToSeller
} else {
a.Tx.DepositTotal += activity.AmountToSeller
}
}
for _, activity := range activities.UnstakeValidatorV1 {
a.Tx.WithdrawalTotal += activity.StakeAmount
}
a.Tx.LastUpdate = now
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(a.Tx.DepositTotal), hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Sum,
a.Address, account.Address, hotspotData.Address, hotspotData.Name,
) )
ch <- prometheus.MustNewConstMetric(
accountWithdrawalsHnt.Desc, accountWithdrawalsHnt.Type, float64(a.Tx.WithdrawalTotal),
a.Address,
)
return nil
} }
func main() { func main() {
fApiUrl := flag.String("apiUrl", "https://api.helium.io", "The helium api url")
fHeliumAccounts := flag.String("accounts", "", "A comma-delimited list of helium accounts to scrape (optional)") fHeliumAccounts := flag.String("accounts", "", "A comma-delimited list of helium accounts to scrape (optional)")
fMetricsPath := flag.String("metricpath", "/metrics", "The metrics path") fMetricsPath := flag.String("metricpath", "/metrics", "The metrics path")
fListenAddress := flag.String("listenAddress", "0.0.0.0", "The http server listen address") fListenAddress := flag.String("listenAddress", "0.0.0.0", "The http server listen address")
fListenPort := flag.String("listenPort", "9865", "The http server listen port") fListenPort := flag.String("listenPort", "9865", "The http server listen port")
flag.Parse() flag.Parse()
heliumapi.ApiUrl = *fApiUrl
heliumAccounts := strings.Split(*fHeliumAccounts, ",") heliumAccounts := strings.Split(*fHeliumAccounts, ",")
serverAddr := *fListenAddress + ":" + *fListenPort serverAddr := *fListenAddress + ":" + *fListenPort
@ -587,5 +640,8 @@ func main() {
http.Handle(*fMetricsPath, promhttp.HandlerFor(r, promhttp.HandlerOpts{})) http.Handle(*fMetricsPath, promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
fmt.Printf("listening on %v\n", serverAddr) fmt.Printf("listening on %v\n", serverAddr)
http.ListenAndServe(serverAddr, nil) if err = http.ListenAndServe(serverAddr, nil); err != nil {
fmt.Println(err)
os.Exit(1)
}
} }

View File

@ -18,13 +18,13 @@ func GetAccountForAddress(account string) (*Account, error) {
} }
// unmarshal the response // unmarshal the response
respobject := Account{} respobject := AccountResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }
func GetActivityForAccount(account string, filterTypes []string, minTime *time.Time, maxTime *time.Time) (*activity.Activities, error) { func GetActivityForAccount(account string, filterTypes []string, minTime *time.Time, maxTime *time.Time) (*activity.Activities, error) {
@ -49,7 +49,7 @@ func GetActivityForAccount(account string, filterTypes []string, minTime *time.T
activityResp := activity.ActivityResp{} activityResp := activity.ActivityResp{}
err = json.Unmarshal(respBody, &activityResp) err = json.Unmarshal(respBody, &activityResp)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
combinedResp.Data = append(combinedResp.Data, activityResp.Data...) combinedResp.Data = append(combinedResp.Data, activityResp.Data...)
} }
@ -67,13 +67,13 @@ func GetActivityCountsForAccount(account string) (*ActivityCounts, error) {
} }
// unmarshal the response // unmarshal the response
respobject := ActivityCounts{} respobject := ActivityCountsResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }
func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) { func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) {
@ -93,16 +93,16 @@ func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time
} }
// unmarshal the response // unmarshal the response
respobject := RewardTotal{} respobject := RewardTotalResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }
func GetHotspotsForAccount(account string) (*AccountHotspots, error) { func GetHotspotsForAccount(account string) (*[]AccountHotspot, error) {
path := "/v1/accounts/" + account + "/hotspots" path := "/v1/accounts/" + account + "/hotspots"
// query the api // query the api
@ -112,11 +112,11 @@ func GetHotspotsForAccount(account string) (*AccountHotspots, error) {
} }
// unmarshal the response // unmarshal the response
respobject := AccountHotspots{} respobject := AccountHotspotsResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }

View File

@ -21,7 +21,6 @@ func NewActivities(resp ActivityResp) (*Activities, error) {
if err := json.Unmarshal(activityRaw, &activityType); err != nil { if err := json.Unmarshal(activityRaw, &activityType); err != nil {
return nil, fmt.Errorf("failed to unmarshal activity: %v", err) return nil, fmt.Errorf("failed to unmarshal activity: %v", err)
} }
fmt.Println(activityType.Type)
switch activityType.Type { switch activityType.Type {
case "add_gateway_v1": case "add_gateway_v1":
addGatewayV1 := AddGatewayV1{} addGatewayV1 := AddGatewayV1{}

View File

@ -16,13 +16,13 @@ func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) {
} }
// unmarshal the response // unmarshal the response
respobject := ActivityCounts{} respobject := ActivityCountsResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }
func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) { func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) {
@ -42,11 +42,11 @@ func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time
} }
// unmarshal the response // unmarshal the response
respobject := RewardTotal{} respobject := RewardTotalResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }

View File

@ -6,7 +6,7 @@ import (
) )
func GetCurrentOraclePrice() (*CurrentOraclePrice, error) { func GetCurrentOraclePrice() (*CurrentOraclePrice, error) {
const path = "/v1/oracle/prices/current" path := "/v1/oracle/prices/current"
// query the api // query the api
respBody, err := getHeliumApi(path, nil) respBody, err := getHeliumApi(path, nil)
@ -15,11 +15,11 @@ func GetCurrentOraclePrice() (*CurrentOraclePrice, error) {
} }
// unmarshal the response // unmarshal the response
respobject := CurrentOraclePrice{} respobject := CurrentOraclePriceResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }

View File

@ -7,16 +7,13 @@ import (
"net/http" "net/http"
) )
const (
apiUrl = "https://api.helium.io"
)
var ( var (
ApiUrl = "https://api.helium.io"
client = &http.Client{} client = &http.Client{}
) )
func createGetRequest(path string, params map[string]string) (*http.Request, error) { func createGetRequest(path string, params map[string]string) (*http.Request, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", apiUrl, path), nil) req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", ApiUrl, path), nil)
// setup headers // setup headers
req.Header.Add("Accept", "application/json") req.Header.Add("Accept", "application/json")
@ -50,14 +47,14 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) {
// query the api // query the api
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to query path %v: %v", path, err) return nil, fmt.Errorf("failed to query %v: %v", path, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
// read the response body // read the response body
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read response body of path %v: %v", path, err) return nil, fmt.Errorf("failed to read response body of %v: %v", path, err)
} }
return body, nil return body, nil
@ -84,14 +81,14 @@ func getHeliumApiWithCursor(path string, params *map[string]string) ([][]byte, e
// query the api // query the api
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to query path %v: %v", path, err) return nil, fmt.Errorf("failed to query %v: %v", path, err)
} }
defer resp.Body.Close() defer resp.Body.Close()
// read the response body and add it to the result array // read the response body and add it to the result array
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read response body of path %v: %v", path, err) return nil, fmt.Errorf("failed to read response body of %v: %v", path, err)
} }
res = append(res, body) res = append(res, body)
@ -99,7 +96,7 @@ func getHeliumApiWithCursor(path string, params *map[string]string) ([][]byte, e
respCursor.Cursor = "" respCursor.Cursor = ""
err = json.Unmarshal(body, &respCursor) err = json.Unmarshal(body, &respCursor)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
// continue querying until there is no longer a cursor // continue querying until there is no longer a cursor

View File

@ -6,7 +6,7 @@ import (
) )
func GetBlockchainStats() (*BlockchainStats, error) { func GetBlockchainStats() (*BlockchainStats, error) {
const path = "/v1/stats" path := "/v1/stats"
// query the api // query the api
respBody, err := getHeliumApi(path, nil) respBody, err := getHeliumApi(path, nil)
@ -15,11 +15,11 @@ func GetBlockchainStats() (*BlockchainStats, error) {
} }
// unmarshal the response // unmarshal the response
respobject := BlockchainStats{} respobject := BlockchainStatsResp{}
err = json.Unmarshal(respBody, &respobject) err = json.Unmarshal(respBody, &respobject)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) return nil, fmt.Errorf("failed to unmarshal response from %v: %v", path, err)
} }
return &respobject, nil return &respobject.Data, nil
} }

View File

@ -1,140 +1,152 @@
package heliumapi package heliumapi
type AccountResp struct {
Data Account `json:"data"`
}
type Account struct { type Account struct {
Data struct { Address string `json:"address"`
Address string `json:"address"` Balance int `json:"balance"`
Balance int `json:"balance"` Block int `json:"block"`
Block int `json:"block"` DCBalance int `json:"dc_balance"`
DCBalance int `json:"dc_balance"` DCNonce int `json:"dc_nonce"`
DCNonce int `json:"dc_nonce"` SECBalance int `json:"sec_balance"`
SECBalance int `json:"sec_balance"` SECNonce int `json:"sec_nonce"`
SECNonce int `json:"sec_nonce"` SpeculativeNonce int `json:"speculative_nonce"`
SpeculativeNonce int `json:"speculative_nonce"`
} `json:"data"`
} }
type AccountHotspots struct { type AccountHotspotsResp struct {
Data []struct { Data []AccountHotspot `json:"data"`
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
TimestampAdded string `json:"timestamp_added"`
Status struct {
Timestamp string `json:"timestamp"`
Online string `json:"online"`
ListenAddrs []string `json:"listen_addrs"`
Height int `json:"height"`
} `json:"status"`
RewardScale float64 `json:"reward_scale"`
Payer string `json:"payer"`
Owner string `json:"owner"`
Nonce int `json:"nonce"`
Name string `json:"name"`
Mode string `json:"mode"`
LocationHex string `json:"location_hex"`
Location string `json:"location"`
LastPocChallenge int `json:"last_poc_challenge"`
LastChangeBlock int `json:"last_change_block"`
Geocode struct {
ShortStreet string `json:"short_street"`
ShortState string `json:"short_state"`
ShortCountry string `json:"short_country"`
ShortCity string `json:"short_city"`
LongStreet string `json:"long_street"`
LongState string `json:"long_state"`
LongCountry string `json:"long_country"`
LongCity string `json:"long_city"`
CityID string `json:"city_id"`
} `json:"geocode"`
Gain int `json:"gain"`
Elevation int `json:"elevation"`
BlockAdded int `json:"block_added"`
Block int `json:"block"`
Address string `json:"address"`
} `json:"data"`
} }
type ActivityCounts struct { type AccountHotspot struct {
Data map[string]int Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
TimestampAdded string `json:"timestamp_added"`
Status struct {
Timestamp string `json:"timestamp"`
Online string `json:"online"`
ListenAddrs []string `json:"listen_addrs"`
Height int `json:"height"`
} `json:"status"`
RewardScale float64 `json:"reward_scale"`
Payer string `json:"payer"`
Owner string `json:"owner"`
Nonce int `json:"nonce"`
Name string `json:"name"`
Mode string `json:"mode"`
LocationHex string `json:"location_hex"`
Location string `json:"location"`
LastPocChallenge int `json:"last_poc_challenge"`
LastChangeBlock int `json:"last_change_block"`
Geocode struct {
ShortStreet string `json:"short_street"`
ShortState string `json:"short_state"`
ShortCountry string `json:"short_country"`
ShortCity string `json:"short_city"`
LongStreet string `json:"long_street"`
LongState string `json:"long_state"`
LongCountry string `json:"long_country"`
LongCity string `json:"long_city"`
CityID string `json:"city_id"`
} `json:"geocode"`
Gain int `json:"gain"`
Elevation int `json:"elevation"`
BlockAdded int `json:"block_added"`
Block int `json:"block"`
Address string `json:"address"`
} }
type RewardTotal struct { type ActivityCountsResp struct {
Data ActivityCounts
}
type ActivityCounts map[string]int
type RewardTotalResp struct {
Meta struct { Meta struct {
MinTime string `json:"min_time"` MinTime string `json:"min_time"`
MaxTime string `json:"max_time"` MaxTime string `json:"max_time"`
} `json:"meta"` } `json:"meta"`
Data struct { Data RewardTotal `json:"data"`
Total float64 `json:"total"` }
Sum float64 `json:"sum"`
Stddev float64 `json:"stddev"` type RewardTotal struct {
Min float64 `json:"min"` Total float64 `json:"total"`
Median float64 `json:"median"` Sum float64 `json:"sum"`
Max float64 `json:"max"` Stddev float64 `json:"stddev"`
Avg float64 `json:"avg"` Min float64 `json:"min"`
} `json:"data"` Median float64 `json:"median"`
Max float64 `json:"max"`
Avg float64 `json:"avg"`
}
type CurrentOraclePriceResp struct {
Data CurrentOraclePrice `json:"data"`
} }
type CurrentOraclePrice struct { type CurrentOraclePrice struct {
Data struct { Price int `json:"price"`
Price int `json:"price"` Block int `json:"block"`
Block int `json:"block"` }
} `json:"data"`
type BlockchainStatsResp struct {
Data BlockchainStats `json:"data"`
} }
type BlockchainStats struct { type BlockchainStats struct {
Data struct { BlockTime struct {
BlockTime struct { LastDay struct {
LastDay struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_day"`
} `json:"last_day"` LastHour struct {
LastHour struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_hour"`
} `json:"last_hour"` LastMonth struct {
LastMonth struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_month"`
} `json:"last_month"` LastWeek struct {
LastWeek struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_week"`
} `json:"last_week"` } `json:"block_times"`
} `json:"block_times"` ChallengeCount struct {
ChallengeCount struct { Active int `json:"active"`
Active int `json:"active"` LastDay int `json:"last_day"`
LastDay int `json:"last_day"` } `json:"challenge_counts"`
} `json:"challenge_counts"` Counts struct {
Counts struct { Validators int `json:"validators"`
Validators int `json:"validators"` Ouis int `json:"ouis"`
Ouis int `json:"ouis"` HotspotsDataonly int `json:"hotspots_dataonly"`
HotspotsDataonly int `json:"hotspots_dataonly"` Blocks int `json:"blocks"`
Blocks int `json:"blocks"` Challenges int `json:"challenges"`
Challenges int `json:"challenges"` Cities int `json:"cities"`
Cities int `json:"cities"` ConsensusGroups int `json:"consensus_groups"`
ConsensusGroups int `json:"consensus_groups"` Countries int `json:"countries"`
Countries int `json:"countries"` Hotspots int `json:"hotspots"`
Hotspots int `json:"hotspots"` Transactions int `json:"transactions"`
Transactions int `json:"transactions"` } `json:"counts"`
} `json:"counts"` ElectionTimes struct {
ElectionTimes struct { LastDay struct {
LastDay struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_day"`
} `json:"last_day"` LastHour struct {
LastHour struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_hour"`
} `json:"last_hour"` LastMonth struct {
LastMonth struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_month"`
} `json:"last_month"` LastWeek struct {
LastWeek struct { Avg float64 `json:"avg"`
Avg float64 `json:"avg"` Stddev float64 `json:"stddev"`
Stddev float64 `json:"stddev"` } `json:"last_week"`
} `json:"last_week"` } `json:"election_times"`
} `json:"election_times"` TokenSupply float64 `json:"token_supply"`
TokenSupply float64 `json:"token_supply"`
} `json:"data"`
} }