diff --git a/helium-blockchain-exporter.go b/helium-blockchain-exporter.go index a0a7b18..7c37666 100644 --- a/helium-blockchain-exporter.go +++ b/helium-blockchain-exporter.go @@ -22,19 +22,46 @@ type metricInfo struct { // Exporter collect metrics from the helium blockchain api and exports them as prometheus metrics. 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 type Account struct { - hotspots []Hotspot - hash string + Address string + Tx AccountTx } -// Hotspot represent a helium hotspot -type Hotspot struct { - name string - hash string +func NewAccount(address string) Account { + return Account{ + Address: address, + Tx: AccountTx{ + DepositTotal: 0, + WithdrawalTotal: 0, + LastUpdate: time.Now(), + // LastUpdate: time.Time{}, + }, + } +} + +type AccountTx struct { + DepositTotal int + WithdrawalTotal int + LastUpdate time.Time } const ( @@ -42,7 +69,24 @@ const ( ) 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"} commonHotspotLabels = append(commonAccountLabels, "hotspot", "hotspot_name") @@ -61,43 +105,43 @@ var ( // helium stats metrics statsValidators = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "validators_total"), + prometheus.BuildFQName(namespace, "stats", "validators"), "The total number of validators.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsOuis = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "ouis_total"), + prometheus.BuildFQName(namespace, "stats", "ouis"), "The total number of organization unique identifiers.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsHotspotsDataOnly = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly_total"), + prometheus.BuildFQName(namespace, "stats", "hotspots_dataonly"), "The total number of data only hotspots.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsBlocks = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "blocks_total"), + prometheus.BuildFQName(namespace, "stats", "blocks"), "The total height/number of blocks in the blockchain.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsChallenges = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "challenges_total"), + prometheus.BuildFQName(namespace, "stats", "challenges"), "The total number of challenges.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsCities = metricInfo{ prometheus.NewDesc( @@ -109,7 +153,7 @@ var ( } statsConsensusGroups = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "consensus_groups_total"), + prometheus.BuildFQName(namespace, "stats", "consensus_groups"), "The total number of consensus groups.", nil, nil, ), @@ -125,15 +169,15 @@ var ( } statsHotspots = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "hotspots_total"), + prometheus.BuildFQName(namespace, "stats", "hotspots"), "The total number of hotspots.", nil, nil, ), - prometheus.CounterValue, + prometheus.GaugeValue, } statsTokenSupply = metricInfo{ prometheus.NewDesc( - prometheus.BuildFQName(namespace, "stats", "token_supply"), + prometheus.BuildFQName(namespace, "stats", "token"), "The total supply of HNT tokens in circulation.", nil, nil, ), @@ -165,22 +209,22 @@ var ( ), prometheus.CounterValue, } - // accountDepositsHnt = metricInfo{ - // prometheus.NewDesc( - // prometheus.BuildFQName(namespace, "account", "deposits_hnt_total"), - // "The number of HNT token deposited to this account.", - // commonAccountLabels, nil, - // ), - // prometheus.CounterValue, - // } - // accountWithdrawalsHnt = metricInfo{ - // prometheus.NewDesc( - // prometheus.BuildFQName(namespace, "account", "withdrawals_hnt_total"), - // "The number of HNT token withdrawn from this account.", - // commonAccountLabels, nil, - // ), - // prometheus.CounterValue, - // } + 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, + } // helium hotspot metrics hotspotUp = metricInfo{ @@ -275,8 +319,8 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { ch <- accountBalanceHnt.Desc ch <- accountActivity.Desc ch <- accountRewardsHnt.Desc - // ch <- accountDepositsHnt.Desc - // ch <- accountWithdrawalsHnt.Desc + ch <- accountDepositsHnt.Desc + ch <- accountWithdrawalsHnt.Desc ch <- hotspotUp.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 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 { fmt.Println(err) return } - accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.hash) + accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.Address) if err != nil { fmt.Println(err) 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 { fmt.Println(err) return @@ -374,23 +423,31 @@ func (e *Exporter) collectAccountMetrics(ch chan<- prometheus.Metric, account *A ch <- prometheus.MustNewConstMetric( accountBalanceHnt.Desc, accountBalanceHnt.Type, float64(accountForAddress.Data.Balance), - account.hash, + account.Address, ) for accType, count := range accountActivityForAddress.Data { ch <- prometheus.MustNewConstMetric( accountActivity.Desc, accountActivity.Type, float64(count), - account.hash, accType, + account.Address, accType, ) } ch <- prometheus.MustNewConstMetric( 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 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 { fmt.Println(err) return @@ -403,7 +460,7 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A return } - hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &time.Time{}, nil) + hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil) if err != nil { fmt.Println(err) return @@ -411,58 +468,52 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A ch <- prometheus.MustNewConstMetric( hotspotUp.Desc, hotspotUp.Type, bool2Float64(hotspotData.Status.Online == "online"), - account.hash, hotspotData.Address, hotspotData.Name, + 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.hash, hotspotData.Address, hotspotData.Name, + account.Address, hotspotData.Address, hotspotData.Name, ) ch <- prometheus.MustNewConstMetric( hotspotBlocks.Desc, hotspotBlocks.Type, float64(hotspotData.Status.Height), - account.hash, hotspotData.Address, hotspotData.Name, + account.Address, hotspotData.Address, hotspotData.Name, ) ch <- prometheus.MustNewConstMetric( hotspotRewardsScale.Desc, hotspotRewardsScale.Type, float64(hotspotData.RewardScale), - account.hash, hotspotData.Address, hotspotData.Name, + account.Address, hotspotData.Address, hotspotData.Name, ) ch <- prometheus.MustNewConstMetric( 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( 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 { ch <- prometheus.MustNewConstMetric( hotspotActivity.Desc, hotspotActivity.Type, float64(count), - account.hash, hotspotData.Address, hotspotData.Name, accType, + account.Address, hotspotData.Address, hotspotData.Name, accType, ) } ch <- prometheus.MustNewConstMetric( 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 NewExporter(accountHashs []string) (*Exporter, error) { - accounts := make([]Account, 0) - for _, accountHash := range accountHashs { - if accountHash != "" { - accounts = append(accounts, NewAccount(accountHash)) - } +func (a *Account) computeTransactions() (*AccountTx, error) { + now := time.Now() + _, err := heliumapi.GetActivityForAccount(a.Address, []string{}, &a.Tx.LastUpdate, &now) + if err != nil { + return nil, err } - return &Exporter{ - Accounts: accounts, - }, nil -} -func NewAccount(hash string) Account { - return Account{ - hash: hash, - } + // fmt.Println(activities) + + a.Tx.LastUpdate = now + return &a.Tx, nil } func main() { diff --git a/heliumapi/accounts.go b/heliumapi/accounts.go index 94c85cd..b521676 100644 --- a/heliumapi/accounts.go +++ b/heliumapi/accounts.go @@ -4,61 +4,10 @@ import ( "encoding/json" "fmt" "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) { path := "/v1/accounts/" + account @@ -78,6 +27,36 @@ func GetAccountForAddress(account string) (*Account, error) { 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, ¶ms) + 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) { path := "/v1/accounts/" + account + "/activity/count" diff --git a/heliumapi/activity/activities.go b/heliumapi/activity/activities.go new file mode 100644 index 0000000..804c335 --- /dev/null +++ b/heliumapi/activity/activities.go @@ -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 +} diff --git a/heliumapi/activity/types.go b/heliumapi/activity/types.go new file mode 100644 index 0000000..6609310 --- /dev/null +++ b/heliumapi/activity/types.go @@ -0,0 +1 @@ +package activity diff --git a/heliumapi/hotspots.go b/heliumapi/hotspots.go index 8de129f..1100f07 100644 --- a/heliumapi/hotspots.go +++ b/heliumapi/hotspots.go @@ -6,27 +6,6 @@ import ( "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) { path := "/v1/hotspots/" + hotspot + "/activity/count" diff --git a/heliumapi/oracleprices.go b/heliumapi/oracleprices.go index ba97a41..5c7f0f7 100644 --- a/heliumapi/oracleprices.go +++ b/heliumapi/oracleprices.go @@ -5,13 +5,6 @@ import ( "fmt" ) -type CurrentOraclePrice struct { - Data struct { - Price int `json:"price"` - Block int `json:"block"` - } `json:"data"` -} - func GetCurrentOraclePrice() (*CurrentOraclePrice, error) { const path = "/v1/oracle/prices/current" diff --git a/heliumapi/query.go b/heliumapi/query.go index 54d5eb7..4a4dced 100644 --- a/heliumapi/query.go +++ b/heliumapi/query.go @@ -1,6 +1,7 @@ package heliumapi import ( + "encoding/json" "fmt" "io/ioutil" "net/http" @@ -14,7 +15,7 @@ var ( 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) // setup headers @@ -23,7 +24,7 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) { // setup query param if there are any if params != nil { query := req.URL.Query() - for k, v := range *params { + for k, v := range params { query.Add(k, v) } if err != nil { @@ -32,6 +33,20 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) { 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 resp, err := client.Do(req) if err != nil { @@ -48,6 +63,56 @@ func getHeliumApi(path string, params *map[string]string) ([]byte, error) { return body, nil } -func getHeliumApiWithCursor(path string, params *map[string]string) ([]byte, error) { - return nil, nil +func getHeliumApiWithCursor(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{} + } + + 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 } diff --git a/heliumapi/stats.go b/heliumapi/stats.go index f93a7a5..b0b6243 100644 --- a/heliumapi/stats.go +++ b/heliumapi/stats.go @@ -5,64 +5,6 @@ import ( "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) { const path = "/v1/stats" diff --git a/heliumapi/types.go b/heliumapi/types.go new file mode 100644 index 0000000..087f5d6 --- /dev/null +++ b/heliumapi/types.go @@ -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"` +}