From 20eddb8fc3ba5bcdf798c0e6a1c6aa1997bb7444 Mon Sep 17 00:00:00 2001 From: Massaki Archambault Date: Sat, 25 Sep 2021 23:38:16 -0400 Subject: [PATCH] make each new query to the helium api be in its own goroutine --- exporter.go | 296 ++++++++++++++++++++++---------------- heliumapi/accounts.go | 16 +-- heliumapi/hotspots.go | 8 +- heliumapi/oracleprices.go | 4 +- heliumapi/stats.go | 4 +- heliumapi/types.go | 246 ++++++++++++++++--------------- 6 files changed, 317 insertions(+), 257 deletions(-) diff --git a/exporter.go b/exporter.go index ef4e5db..282e884 100644 --- a/exporter.go +++ b/exporter.go @@ -7,6 +7,7 @@ import ( "net/http" "strconv" "strings" + "sync" "time" "github.com/helium-blockchain-exporter/heliumapi" @@ -46,6 +47,13 @@ type Account struct { Tx AccountTx } +type AccountTx struct { + DepositTotal int + WithdrawalTotal int + LastUpdate time.Time + UpdateLock sync.Mutex +} + func NewAccount(address string) Account { return Account{ Address: address, @@ -57,12 +65,6 @@ func NewAccount(address string) Account { } } -type AccountTx struct { - DepositTotal int - WithdrawalTotal int - LastUpdate time.Time -} - const ( namespace = "helium" ) @@ -317,16 +319,27 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) { // 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) { - e.collectOracleMetrics(ch) - e.collectStatsMetrics(ch) + wg := new(sync.WaitGroup) + + wg.Add(2) + go e.collectOracleMetrics(wg, ch) + e.collectStatsMetrics(wg, ch) for i := range e.Accounts { - e.collectAccountMetrics(ch, &e.Accounts[i]) - e.collectHotspotMetrics(ch, &e.Accounts[i]) + 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]) } + wg.Wait() } // 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() if err != nil { fmt.Println(err) @@ -339,7 +352,9 @@ func (e *Exporter) collectOracleMetrics(ch chan<- prometheus.Metric) { } // 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() if err != nil { fmt.Println(err) @@ -379,48 +394,145 @@ 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) { +func (e *Exporter) collectAccountMetrics(wg *sync.WaitGroup, ch chan<- prometheus.Metric, account *Account) { + defer wg.Done() + accountForAddress, err := heliumapi.GetAccountForAddress(account.Address) if err != nil { fmt.Println(err) return } - accountActivityForAddress, err := heliumapi.GetActivityCountsForAccount(account.Address) - if err != nil { - fmt.Println(err) - return - } - - accountRewardTotalsForAddress, err := heliumapi.GetRewardTotalsForAccount(account.Address, &e.StartTime, nil) - 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, ) +} + +// 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) + if err != nil { + fmt.Println(err) + return + } + for accType, count := range accountActivityForAddress.Data { ch <- prometheus.MustNewConstMetric( accountActivity.Desc, accountActivity.Type, float64(count), 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( accountRewardsHnt.Desc, accountRewardsHnt.Type, accountRewardTotalsForAddress.Data.Sum, account.Address, ) } -// collectStatsMetrics collect metrics in the hotspot group from the helium api -func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *Account) { +// 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() + + // 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 + } + + // logic 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) if err != nil { fmt.Println(err) @@ -428,17 +540,10 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A } for _, hotspotData := range hotspotsForAddress.Data { - hotspotActivityForAddress, err := heliumapi.GetHotspotActivityCount(hotspotData.Address) - if err != nil { - fmt.Println(err) - return - } - - hotspotRewardTotalsForAddress, err := heliumapi.GetRewardsTotalForHotspot(hotspotData.Address, &e.StartTime, nil) - if err != nil { - fmt.Println(err) - return - } + // 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"), @@ -464,96 +569,41 @@ func (e *Exporter) collectHotspotMetrics(ch chan<- prometheus.Metric, account *A hotspotAntennaInfo.Desc, hotspotAntennaInfo.Type, 1.0, 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.Data { ch <- prometheus.MustNewConstMetric( - hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Data.Sum, - account.Address, hotspotData.Address, hotspotData.Name, + hotspotActivity.Desc, hotspotActivity.Type, float64(count), + account.Address, hotspotData.Address, hotspotData.Name, accType, ) } } -func (a *Account) collectTransactionMetrics(ch chan<- prometheus.Metric) error { - now := time.Now() - activities, err := heliumapi.GetActivityForAccount(a.Address, []string{}, &a.Tx.LastUpdate, &now) +// 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 { - 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( - accountDepositsHnt.Desc, accountDepositsHnt.Type, float64(a.Tx.DepositTotal), - a.Address, + hotspotRewardsHnt.Desc, hotspotRewardsHnt.Type, hotspotRewardTotalsForAddress.Data.Sum, + account.Address, hotspotData.Address, hotspotData.Name, ) - ch <- prometheus.MustNewConstMetric( - accountWithdrawalsHnt.Desc, accountWithdrawalsHnt.Type, float64(a.Tx.WithdrawalTotal), - a.Address, - ) - - return nil } func main() { diff --git a/heliumapi/accounts.go b/heliumapi/accounts.go index b521676..372bbcd 100644 --- a/heliumapi/accounts.go +++ b/heliumapi/accounts.go @@ -8,7 +8,7 @@ import ( "github.com/helium-blockchain-exporter/heliumapi/activity" ) -func GetAccountForAddress(account string) (*Account, error) { +func GetAccountForAddress(account string) (*AccountResp, error) { path := "/v1/accounts/" + account // query the api @@ -18,7 +18,7 @@ func GetAccountForAddress(account string) (*Account, error) { } // unmarshal the response - respobject := Account{} + respobject := AccountResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) @@ -57,7 +57,7 @@ func GetActivityForAccount(account string, filterTypes []string, minTime *time.T return activity.NewActivities(combinedResp) } -func GetActivityCountsForAccount(account string) (*ActivityCounts, error) { +func GetActivityCountsForAccount(account string) (*ActivityCountsResp, error) { path := "/v1/accounts/" + account + "/activity/count" // query the api @@ -67,7 +67,7 @@ func GetActivityCountsForAccount(account string) (*ActivityCounts, error) { } // unmarshal the response - respobject := ActivityCounts{} + respobject := ActivityCountsResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) @@ -76,7 +76,7 @@ func GetActivityCountsForAccount(account string) (*ActivityCounts, error) { return &respobject, nil } -func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) { +func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time.Time) (*RewardTotalResp, error) { path := "/v1/accounts/" + account + "/rewards/sum" params := map[string]string{} if minTime != nil { @@ -93,7 +93,7 @@ func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time } // unmarshal the response - respobject := RewardTotal{} + respobject := RewardTotalResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) @@ -102,7 +102,7 @@ func GetRewardTotalsForAccount(account string, minTime *time.Time, maxTime *time return &respobject, nil } -func GetHotspotsForAccount(account string) (*AccountHotspots, error) { +func GetHotspotsForAccount(account string) (*AccountHotspotsResp, error) { path := "/v1/accounts/" + account + "/hotspots" // query the api @@ -112,7 +112,7 @@ func GetHotspotsForAccount(account string) (*AccountHotspots, error) { } // unmarshal the response - respobject := AccountHotspots{} + respobject := AccountHotspotsResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) diff --git a/heliumapi/hotspots.go b/heliumapi/hotspots.go index 1100f07..0877d84 100644 --- a/heliumapi/hotspots.go +++ b/heliumapi/hotspots.go @@ -6,7 +6,7 @@ import ( "time" ) -func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) { +func GetHotspotActivityCount(hotspot string) (*ActivityCountsResp, error) { path := "/v1/hotspots/" + hotspot + "/activity/count" // query the api @@ -16,7 +16,7 @@ func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) { } // unmarshal the response - respobject := ActivityCounts{} + respobject := ActivityCountsResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) @@ -25,7 +25,7 @@ func GetHotspotActivityCount(hotspot string) (*ActivityCounts, error) { return &respobject, nil } -func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time.Time) (*RewardTotal, error) { +func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time.Time) (*RewardTotalResp, error) { path := "/v1/hotspots/" + hotspot + "/rewards/sum" params := map[string]string{} if minTime != nil { @@ -42,7 +42,7 @@ func GetRewardsTotalForHotspot(hotspot string, minTime *time.Time, maxTime *time } // unmarshal the response - respobject := RewardTotal{} + respobject := RewardTotalResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) diff --git a/heliumapi/oracleprices.go b/heliumapi/oracleprices.go index 5c7f0f7..4339a37 100644 --- a/heliumapi/oracleprices.go +++ b/heliumapi/oracleprices.go @@ -5,7 +5,7 @@ import ( "fmt" ) -func GetCurrentOraclePrice() (*CurrentOraclePrice, error) { +func GetCurrentOraclePrice() (*CurrentOraclePriceResp, error) { const path = "/v1/oracle/prices/current" // query the api @@ -15,7 +15,7 @@ func GetCurrentOraclePrice() (*CurrentOraclePrice, error) { } // unmarshal the response - respobject := CurrentOraclePrice{} + respobject := CurrentOraclePriceResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) diff --git a/heliumapi/stats.go b/heliumapi/stats.go index b0b6243..429598f 100644 --- a/heliumapi/stats.go +++ b/heliumapi/stats.go @@ -5,7 +5,7 @@ import ( "fmt" ) -func GetBlockchainStats() (*BlockchainStats, error) { +func GetBlockchainStats() (*BlockchainStatsResp, error) { const path = "/v1/stats" // query the api @@ -15,7 +15,7 @@ func GetBlockchainStats() (*BlockchainStats, error) { } // unmarshal the response - respobject := BlockchainStats{} + respobject := BlockchainStatsResp{} err = json.Unmarshal(respBody, &respobject) if err != nil { return nil, fmt.Errorf("failed to unmarshal response from path %v: %v", path, err) diff --git a/heliumapi/types.go b/heliumapi/types.go index 5c41228..c6d2440 100644 --- a/heliumapi/types.go +++ b/heliumapi/types.go @@ -1,140 +1,150 @@ package heliumapi +type AccountResp struct { + Data Account `json:"data"` +} + 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"` + 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"` } -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 AccountHotspotsResp struct { + Data []AccountHotspot `json:"data"` } -type ActivityCounts struct { +type AccountHotspot 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"` +} + +type ActivityCountsResp struct { Data map[string]int } -type RewardTotal struct { +type RewardTotalResp 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"` + Data RewardTotal `json:"data"` +} + +type RewardTotal 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"` +} + +type CurrentOraclePriceResp struct { + Data CurrentOraclePrice `json:"data"` } type CurrentOraclePrice struct { - Data struct { - Price int `json:"price"` - Block int `json:"block"` - } `json:"data"` + Price int `json:"price"` + Block int `json:"block"` +} + +type BlockchainStatsResp struct { + Data BlockchainStats `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"` + 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"` }