1
0
Fork 0

refactor code organization

This commit is contained in:
Massaki Archambault 2021-09-24 21:30:22 -04:00
parent 7f8b73055e
commit b8172f06ab
9 changed files with 413 additions and 214 deletions

View File

@ -22,19 +22,46 @@ type metricInfo struct {
// Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics. // Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics.
type Exporter struct { type Exporter struct {
Accounts []Account 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
} }
// Account represents a helium account // Account represents a helium account
type Account struct { type Account struct {
hotspots []Hotspot Address string
hash string Tx AccountTx
} }
// Hotspot represent a helium hotspot func NewAccount(address string) Account {
type Hotspot struct { return Account{
name string Address: address,
hash string Tx: AccountTx{
DepositTotal: 0,
WithdrawalTotal: 0,
LastUpdate: time.Now(),
// LastUpdate: time.Time{},
},
}
}
type AccountTx struct {
DepositTotal int
WithdrawalTotal int
LastUpdate time.Time
} }
const ( const (
@ -42,7 +69,24 @@ const (
) )
var ( var (
// lables // Activity classification
accountDepositActivities = []string{
"payment_v1", // if payee == account_address
"payment_v2", // if payee == account_address
"rewards_v1",
"unstake_validator_v1",
}
accountWithdrawalActivities = []string{
"add_gateway_v1",
"assert_location_v1",
"assert_location_v2",
"payment_v1", // if payer == account_address
"payment_v2", // if payer == account_address
"stake_validator_v1",
"token_burn_v1",
}
// labels
commonAccountLabels = []string{"account"} commonAccountLabels = []string{"account"}
commonHotspotLabels = append(commonAccountLabels, "hotspot", "hotspot_name") commonHotspotLabels = append(commonAccountLabels, "hotspot", "hotspot_name")
@ -61,43 +105,43 @@ var (
// helium stats metrics // helium stats metrics
statsValidators = metricInfo{ statsValidators = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "validators_total"), prometheus.BuildFQName(namespace, "stats", "validators"),
"The total number of validators.", "The total number of validators.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsOuis = metricInfo{ statsOuis = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "ouis_total"), prometheus.BuildFQName(namespace, "stats", "ouis"),
"The total number of organization unique identifiers.", "The total number of organization unique identifiers.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsHotspotsDataOnly = metricInfo{ statsHotspotsDataOnly = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly_total"), prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly"),
"The total number of data only hotspots.", "The total number of data only hotspots.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsBlocks = metricInfo{ statsBlocks = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "blocks_total"), prometheus.BuildFQName(namespace, "stats", "blocks"),
"The total height/number of blocks in the blockchain.", "The total height/number of blocks in the blockchain.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsChallenges = metricInfo{ statsChallenges = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "challenges_total"), prometheus.BuildFQName(namespace, "stats", "challenges"),
"The total number of challenges.", "The total number of challenges.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsCities = metricInfo{ statsCities = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
@ -109,7 +153,7 @@ var (
} }
statsConsensusGroups = metricInfo{ statsConsensusGroups = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "consensus_groups_total"), prometheus.BuildFQName(namespace, "stats", "consensus_groups"),
"The total number of consensus groups.", "The total number of consensus groups.",
nil, nil, nil, nil,
), ),
@ -125,15 +169,15 @@ var (
} }
statsHotspots = metricInfo{ statsHotspots = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "hotspots_total"), prometheus.BuildFQName(namespace, "stats", "hotspots"),
"The total number of hotspots.", "The total number of hotspots.",
nil, nil, nil, nil,
), ),
prometheus.CounterValue, prometheus.GaugeValue,
} }
statsTokenSupply = metricInfo{ statsTokenSupply = metricInfo{
prometheus.NewDesc( prometheus.NewDesc(
prometheus.BuildFQName(namespace, "stats", "token_supply"), prometheus.BuildFQName(namespace, "stats", "token"),
"The total supply of HNT tokens in circulation.", "The total supply of HNT tokens in circulation.",
nil, nil, nil, nil,
), ),
@ -165,22 +209,22 @@ var (
), ),
prometheus.CounterValue, prometheus.CounterValue,
} }
// accountDepositsHnt = metricInfo{ accountDepositsHnt = metricInfo{
// prometheus.NewDesc( prometheus.NewDesc(
// prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"), prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"),
// "The number of HNT token deposited to this account.", "The number of HNT tokens deposited to this account.",
// commonAccountLabels, nil, commonAccountLabels, nil,
// ), ),
// prometheus.CounterValue, prometheus.CounterValue,
// } }
// accountWithdrawalsHnt = metricInfo{ accountWithdrawalsHnt = metricInfo{
// prometheus.NewDesc( prometheus.NewDesc(
// prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"), prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"),
// "The number of HNT token withdrawn from this account.", "The number of HNT tokens withdrawn from this account.",
// commonAccountLabels, nil, commonAccountLabels, nil,
// ), ),
// prometheus.CounterValue, prometheus.CounterValue,
// } }
// helium hotspot metrics // helium hotspot metrics
hotspotUp = metricInfo{ hotspotUp = metricInfo{
@ -275,8 +319,8 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
ch <- accountBalanceHnt.Desc ch <- accountBalanceHnt.Desc
ch <- accountActivity.Desc ch <- accountActivity.Desc
ch <- accountRewardsHnt.Desc ch <- accountRewardsHnt.Desc
// ch <- accountDepositsHnt.Desc ch <- accountDepositsHnt.Desc
// ch <- accountWithdrawalsHnt.Desc ch <- accountWithdrawalsHnt.Desc
ch <- hotspotUp.Desc ch <- hotspotUp.Desc
ch <- hotspotRelayed.Desc ch <- hotspotRelayed.Desc
@ -354,19 +398,24 @@ func (e *Exporter) collectStatsMetrics(ch chan<- prometheus.Metric) {
// 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(ch chan<- prometheus.Metric, account *Account) {
accountForAddress, err := heliumapi.GetAccountForAddress(account.hash) accountForAddress, err := heliumapi.GetAccountForAddress(account.Address)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
} }
accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.hash) 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.hash, &time.Time{}, nil) accountRewardTotalsForAddress, err := heliumapi.GetRewardTotalsForAccount(account.Address, &e.StartTime, nil)
if err != nil {
fmt.Println(err)
return
}
tx, err := account.computeTransactions()
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -374,23 +423,31 @@ func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account *A
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Data.Balance), accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Data.Balance),
account.hash, account.Address,
) )
for accType, count := range accountActivityForAddress.Data { 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.hash, accType, account.Address, accType,
) )
} }
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Data.Sum, accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Data.Sum,
account.hash, account.Address,
)
ch <- prometheus.MustNewConstMetric(
accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(tx.DepositTotal),
account.Address,
)
ch <- prometheus.MustNewConstMetric(
accountWithdrawalsHnt.Desc, accountWithdrawalsHnt.Type, float64(tx.WithdrawalTotal),
account.Address,
) )
} }
// collectStatsMetrics collect metrics in the hotspot group from the helium api // collectStatsMetrics collect metrics in the hotspot group from the helium api
func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *Account) { func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *Account) {
hotspotsForAddress, err := heliumapi.GetHotspotsForAccount(account.hash) hotspotsForAddress, err := heliumapi.GetHotspotsForAccount(account.Address)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -403,7 +460,7 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A
return return
} }
hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &time.Time{}, nil) hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
return return
@ -411,58 +468,52 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"), hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"),
account.hash, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotRelayed.Desc, hotspotRelayed.Type, bool2Float64(len(hotspotData.Status.ListenAddrs) > 0 && strings.HasPrefix(hotspotData.Status.ListenAddrs[0], "/p2p")), hotspotRelayed.Desc, hotspotRelayed.Type, bool2Float64(len(hotspotData.Status.ListenAddrs) > 0 && strings.HasPrefix(hotspotData.Status.ListenAddrs[0], "/p2p")),
account.hash, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotBlocks.Desc, hotspotBlocks.Type, float64(hotspotData.Status.Height), hotspotBlocks.Desc, hotspotBlocks.Type, float64(hotspotData.Status.Height),
account.hash, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotRewardsScale.Desc, hotspotRewardsScale.Type, float64(hotspotData.RewardScale), hotspotRewardsScale.Desc, hotspotRewardsScale.Type, float64(hotspotData.RewardScale),
account.hash, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name,
) )
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotGeocodeInfo.Desc, hotspotGeocodeInfo.Type, 1.0, hotspotGeocodeInfo.Desc, hotspotGeocodeInfo.Type, 1.0,
account.hash, 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, 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( ch <- prometheus.MustNewConstMetric(
hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0, hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0,
account.hash, 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 { for accType, count := range hotspotActivityForAddress.Data {
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotActivity.Desc, hotspotActivity.Type, float64(count), hotspotActivity.Desc, hotspotActivity.Type, float64(count),
account.hash, hotspotData.Address, hotspotData.Name, accType, account.Address, hotspotData.Address, hotspotData.Name, accType,
) )
} }
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Data.Sum, hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Data.Sum,
account.hash, hotspotData.Address, hotspotData.Name, account.Address, hotspotData.Address, hotspotData.Name,
) )
} }
} }
// NewExporter returns an initialized Exporter func (a *Account) computeTransactions() (*AccountTx, error) {
func NewExporter(accountHashs []string) (*Exporter, error) { now := time.Now()
accounts := make([]Account, 0) _, err := heliumapi.GetActivityForAccount(a.Address, []string{}, &a.Tx.LastUpdate, &now)
for _, accountHash := range accountHashs { if err != nil {
if accountHash != "" { return nil, err
accounts = append(accounts, NewAccount(accountHash))
}
} }
return &Exporter{
Accounts: accounts,
}, nil
}
func NewAccount(hash string) Account { // fmt.Println(activities)
return Account{
hash: hash, a.Tx.LastUpdate = now
} return &a.Tx, nil
} }
func main() { func main() {

View File

@ -4,61 +4,10 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"time" "time"
"github.com/helium-blockchain-exporter/heliumapi/activity"
) )
type Account struct {
Data struct {
Address string `json:"address"`
Balance int `json:"balance"`
Block int `json:"block"`
DCBalance int `json:"dc_balance"`
DCNonce int `json:"dc_nonce"`
SECBalance int `json:"sec_balance"`
SECNonce int `json:"sec_nonce"`
SpeculativeNonce int `json:"speculative_nonce"`
} `json:"data"`
}
type AccountHotspots struct {
Data []struct {
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"`
}
func GetAccountForAddress(account string) (*Account, error) { func GetAccountForAddress(account string) (*Account, error) {
path := "/v1/accounts/" + account path := "/v1/accounts/" + account
@ -78,6 +27,36 @@ func GetAccountForAddress(account string) (*Account, error) {
return &respobject, nil return &respobject, nil
} }
func GetActivityForAccount(account string, filterTypes []string, minTime *time.Time, maxTime *time.Time) (*activity.Activities, error) {
path := "/v1/accounts/" + account + "/activity"
params := map[string]string{}
if minTime != nil {
params["min_time"] = minTime.UTC().Format("2006-01-02T15:04:05Z")
}
if maxTime != nil {
params["max_time"] = maxTime.UTC().Format("2006-01-02T15:04:05Z")
}
// query the api
resBodies, err := getHeliumApiWithCursor(path, &params)
if err != nil {
return nil, err
}
// unmarshal the responses and merge them all together
combinedResp := activity.ActivityResp{}
for _, respBody := range resBodies {
activityResp := activity.ActivityResp{}
err = json.Unmarshal(respBody, &activityResp)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err)
}
combinedResp.Data = append(combinedResp.Data, activityResp.Data...)
}
return activity.NewActivities(combinedResp)
}
func GetActivityCountsForAccount(account string) (*ActivityCounts, error) { func GetActivityCountsForAccount(account string) (*ActivityCounts, error) {
path := "/v1/accounts/" + account + "/activity/count" path := "/v1/accounts/" + account + "/activity/count"

View File

@ -0,0 +1,43 @@
package activity
import (
"encoding/json"
"fmt"
)
type ActivityResp struct {
Data []json.RawMessage `json:"data"`
}
type Activities struct{}
func NewActivities(resp ActivityResp) (*Activities, error) {
type ActivityType struct {
Type string `json:"type"`
}
activityType := ActivityType{}
activities := Activities{}
for _, activityRaw := range resp.Data {
if err := json.Unmarshal(activityRaw, &activityType); err != nil {
return nil, fmt.Errorf("failed to unmarshal activity: %v", err)
}
fmt.Println(activityType.Type)
switch activityType.Type {
case "add_gateway_v1":
case "assert_location_v1":
case "assert_location_v2":
case "payment_v1":
case "payment_v2":
case "rewards_v1":
case "stake_validator_v1":
case "token_burn_v1":
case "unstake_validator_v1":
default:
fmt.Printf("unimplemented activy type: %v", activityType.Type)
}
}
return &activities, nil
}

View File

@ -0,0 +1 @@
package activity

View File

@ -6,27 +6,6 @@ import (
"time" "time"
) )
type ActivityCounts struct {
Data map[string]int
}
type RewardTotal struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data struct {
Total float64 `json:"total"`
Sum float64 `json:"sum"`
Stddev float64 `json:"stddev"`
Min float64 `json:"min"`
Median float64 `json:"median"`
Max float64 `json:"max"`
Avg float64 `json:"avg"`
} `json:"data"`
}
func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) { func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) {
path := "/v1/hotspots/" + hotspot + "/activity/count" path := "/v1/hotspots/" + hotspot + "/activity/count"

View File

@ -5,13 +5,6 @@ import (
"fmt" "fmt"
) )
type CurrentOraclePrice struct {
Data struct {
Price int `json:"price"`
Block int `json:"block"`
} `json:"data"`
}
func GetCurrentOraclePrice() (*CurrentOraclePrice, error) { func GetCurrentOraclePrice() (*CurrentOraclePrice, error) {
const path = "/v1/oracle/prices/current" const path = "/v1/oracle/prices/current"

View File

@ -1,6 +1,7 @@
package heliumapi package heliumapi
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -14,7 +15,7 @@ var (
client = &http.Client{} client = &http.Client{}
) )
func getHeliumApi(path string, params *map[string]string) ([]byte, 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
@ -23,7 +24,7 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) {
// setup query param if there are any // setup query param if there are any
if params != nil { if params != nil {
query := req.URL.Query() query := req.URL.Query()
for k, v := range *params { for k, v := range params {
query.Add(k, v) query.Add(k, v)
} }
if err != nil { if err != nil {
@ -32,6 +33,20 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) {
req.URL.RawQuery = query.Encode() req.URL.RawQuery = query.Encode()
} }
return req, err
}
func getHeliumApi(path string, params *map[string]string) ([]byte, error) {
// if params is nil, set it to an empty map
if params == nil {
params = &map[string]string{}
}
req, err := createGetRequest(path, *params)
if err != nil {
return nil, fmt.Errorf("failed to create query request %v: %v", path, err)
}
// query the api // query the api
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
@ -48,6 +63,56 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) {
return body, nil return body, nil
} }
func getHeliumApiWithCursor(path string, params *map[string]string) ([]byte, error) { func getHeliumApiWithCursor(path string, params *map[string]string) ([][]byte, error) {
return nil, nil // if params is nil, set it to an empty map
if params == nil {
params = &map[string]string{}
}
type ResponseWithCursor struct {
Cursor string `json:"cursor"`
}
res := [][]byte{}
respCursor := ResponseWithCursor{}
req, err := createGetRequest(path, *params)
if err != nil {
return nil, fmt.Errorf("failed to create query request %v: %v", path, err)
}
for {
// query the api
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to query path %v: %v", path, err)
}
defer resp.Body.Close()
// read the response body and add it to the result array
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body of path %v: %v", path, err)
}
res = append(res, body)
// parse the response body for a cursor
respCursor.Cursor = ""
err = json.Unmarshal(body, &respCursor)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err)
}
// continue querying until there is no longer a cursor
if respCursor.Cursor != "" {
params = &map[string]string{"cursor": respCursor.Cursor}
req, err = createGetRequest(path, *params)
if err != nil {
return nil, fmt.Errorf("failed to create query request %v: %v", path, err)
}
} else {
break
}
}
return res, nil
} }

View File

@ -5,64 +5,6 @@ import (
"fmt" "fmt"
) )
type BlockchainStats struct {
Data struct {
BlockTime struct {
LastDay struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_day"`
LastHour struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_hour"`
LastMonth struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_month"`
LastWeek struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_week"`
} `json:"block_times"`
ChallengeCount struct {
Active int `json:"active"`
LastDay int `json:"last_day"`
} `json:"challenge_counts"`
Counts struct {
Validators int `json:"validators"`
Ouis int `json:"ouis"`
HotspotsDataonly int `json:"hotspots_dataonly"`
Blocks int `json:"blocks"`
Challenges int `json:"challenges"`
Cities int `json:"cities"`
ConsensusGroups int `json:"consensus_groups"`
Countries int `json:"countries"`
Hotspots int `json:"hotspots"`
Transactions int `json:"transactions"`
} `json:"counts"`
ElectionTimes struct {
LastDay struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_day"`
LastHour struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_hour"`
LastMonth struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_month"`
LastWeek struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_week"`
} `json:"election_times"`
TokenSupply float64 `json:"token_supply"`
} `json:"data"`
}
func GetBlockchainStats() (*BlockchainStats, error) { func GetBlockchainStats() (*BlockchainStats, error) {
const path = "/v1/stats" const path = "/v1/stats"

146
heliumapi/types.go Normal file
View File

@ -0,0 +1,146 @@
package heliumapi
type Account struct {
Data struct {
Address string `json:"address"`
Balance int `json:"balance"`
Block int `json:"block"`
DCBalance int `json:"dc_balance"`
DCNonce int `json:"dc_nonce"`
SECBalance int `json:"sec_balance"`
SECNonce int `json:"sec_nonce"`
SpeculativeNonce int `json:"speculative_nonce"`
} `json:"data"`
}
// Time int `json:"time"`
// StartEpoch int `json:"start_epoch"`
// Height int `json:"height"`
// Hash string `json:"hash"`
// EndEpoch int `json:"end_epoch"`
type AccountHotspots struct {
Data []struct {
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 {
Data map[string]int
}
type RewardTotal struct {
Meta struct {
MinTime string `json:"min_time"`
MaxTime string `json:"max_time"`
} `json:"meta"`
Data struct {
Total float64 `json:"total"`
Sum float64 `json:"sum"`
Stddev float64 `json:"stddev"`
Min float64 `json:"min"`
Median float64 `json:"median"`
Max float64 `json:"max"`
Avg float64 `json:"avg"`
} `json:"data"`
}
type CurrentOraclePrice struct {
Data struct {
Price int `json:"price"`
Block int `json:"block"`
} `json:"data"`
}
type BlockchainStats struct {
Data struct {
BlockTime struct {
LastDay struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_day"`
LastHour struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_hour"`
LastMonth struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_month"`
LastWeek struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_week"`
} `json:"block_times"`
ChallengeCount struct {
Active int `json:"active"`
LastDay int `json:"last_day"`
} `json:"challenge_counts"`
Counts struct {
Validators int `json:"validators"`
Ouis int `json:"ouis"`
HotspotsDataonly int `json:"hotspots_dataonly"`
Blocks int `json:"blocks"`
Challenges int `json:"challenges"`
Cities int `json:"cities"`
ConsensusGroups int `json:"consensus_groups"`
Countries int `json:"countries"`
Hotspots int `json:"hotspots"`
Transactions int `json:"transactions"`
} `json:"counts"`
ElectionTimes struct {
LastDay struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_day"`
LastHour struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_hour"`
LastMonth struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_month"`
LastWeek struct {
Avg float64 `json:"avg"`
Stddev float64 `json:"stddev"`
} `json:"last_week"`
} `json:"election_times"`
TokenSupply float64 `json:"token_supply"`
} `json:"data"`
}