diff --git a/api/types.go b/api/types.go index c44bab20..99203f16 100644 --- a/api/types.go +++ b/api/types.go @@ -431,24 +431,29 @@ type BlockRaw struct { // BlockbookInfo contains information about the running blockbook instance type BlockbookInfo struct { - Coin string `json:"coin"` - Host string `json:"host"` - Version string `json:"version"` - GitCommit string `json:"gitCommit"` - BuildTime string `json:"buildTime"` - SyncMode bool `json:"syncMode"` - InitialSync bool `json:"initialSync"` - InSync bool `json:"inSync"` - BestHeight uint32 `json:"bestHeight"` - LastBlockTime time.Time `json:"lastBlockTime"` - InSyncMempool bool `json:"inSyncMempool"` - LastMempoolTime time.Time `json:"lastMempoolTime"` - MempoolSize int `json:"mempoolSize"` - Decimals int `json:"decimals"` - DbSize int64 `json:"dbSize"` - DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` - DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` - About string `json:"about"` + Coin string `json:"coin"` + Host string `json:"host"` + Version string `json:"version"` + GitCommit string `json:"gitCommit"` + BuildTime string `json:"buildTime"` + SyncMode bool `json:"syncMode"` + InitialSync bool `json:"initialSync"` + InSync bool `json:"inSync"` + BestHeight uint32 `json:"bestHeight"` + LastBlockTime time.Time `json:"lastBlockTime"` + InSyncMempool bool `json:"inSyncMempool"` + LastMempoolTime time.Time `json:"lastMempoolTime"` + MempoolSize int `json:"mempoolSize"` + Decimals int `json:"decimals"` + DbSize int64 `json:"dbSize"` + HasFiatRates bool `json:"hasFiatRates,omitempty"` + HasTokenFiatRates bool `json:"hasTokenFiatRates,omitempty"` + CurrentFiatRatesTime *time.Time `json:"currentFiatRatesTime,omitempty"` + HistoricalFiatRatesTime *time.Time `json:"historicalFiatRatesTime,omitempty"` + HistoricalTokenFiatRatesTime *time.Time `json:"historicalTokenFiatRatesTime,omitempty"` + DbSizeFromColumns int64 `json:"dbSizeFromColumns,omitempty"` + DbColumns []common.InternalStateColumn `json:"dbColumns,omitempty"` + About string `json:"about"` } // SystemInfo contains information about the running blockbook and backend instance diff --git a/api/worker.go b/api/worker.go index f95ca93e..03809bb5 100644 --- a/api/worker.go +++ b/api/worker.go @@ -134,9 +134,11 @@ func aggregateAddresses(m map[string]struct{}, addresses []string, isAddress boo } func (w *Worker) newAddressesMapForAliases() map[string]struct{} { + // return non nil map only if the chain supports address aliases if w.useAddressAliases { return make(map[string]struct{}) } + // returning nil disables the processing of the address aliases return nil } @@ -2034,6 +2036,13 @@ func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Sig return nil } +func nonZeroTime(t time.Time) *time.Time { + if t.IsZero() { + return nil + } + return &t +} + // GetSystemInfo returns information about system func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { start := time.Now() @@ -2057,24 +2066,29 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) { internalDBSize = w.is.DBSizeTotal() } blockbookInfo := &BlockbookInfo{ - Coin: w.is.Coin, - Host: w.is.Host, - Version: vi.Version, - GitCommit: vi.GitCommit, - BuildTime: vi.BuildTime, - SyncMode: w.is.SyncMode, - InitialSync: w.is.InitialSync, - InSync: inSync, - BestHeight: bestHeight, - LastBlockTime: lastBlockTime, - InSyncMempool: inSyncMempool, - LastMempoolTime: lastMempoolTime, - MempoolSize: mempoolSize, - Decimals: w.chainParser.AmountDecimals(), - DbSize: w.db.DatabaseSizeOnDisk(), - DbSizeFromColumns: internalDBSize, - DbColumns: columnStats, - About: Text.BlockbookAbout, + Coin: w.is.Coin, + Host: w.is.Host, + Version: vi.Version, + GitCommit: vi.GitCommit, + BuildTime: vi.BuildTime, + SyncMode: w.is.SyncMode, + InitialSync: w.is.InitialSync, + InSync: inSync, + BestHeight: bestHeight, + LastBlockTime: lastBlockTime, + InSyncMempool: inSyncMempool, + LastMempoolTime: lastMempoolTime, + MempoolSize: mempoolSize, + Decimals: w.chainParser.AmountDecimals(), + HasFiatRates: w.is.HasFiatRates, + HasTokenFiatRates: w.is.HasTokenFiatRates, + CurrentFiatRatesTime: nonZeroTime(w.is.CurrentFiatRatesTime), + HistoricalFiatRatesTime: nonZeroTime(w.is.HistoricalFiatRatesTime), + HistoricalTokenFiatRatesTime: nonZeroTime(w.is.HistoricalTokenFiatRatesTime), + DbSize: w.db.DatabaseSizeOnDisk(), + DbSizeFromColumns: internalDBSize, + DbColumns: columnStats, + About: Text.BlockbookAbout, } backendInfo := &common.BackendInfo{ BackendError: backendError, diff --git a/common/internalstate.go b/common/internalstate.go index a7090c80..6be529ec 100644 --- a/common/internalstate.go +++ b/common/internalstate.go @@ -77,6 +77,13 @@ type InternalState struct { UtxoChecked bool `json:"utxoChecked"` + // store only the historical state, not the current state of the fiat rates in DB + HasFiatRates bool `json:"-"` + HasTokenFiatRates bool `json:"-"` + CurrentFiatRatesTime time.Time `json:"-"` + HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"` + HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime"` + BackendInfo BackendInfo `json:"-"` } diff --git a/db/rocksdb.go b/db/rocksdb.go index 5f72e6b7..84bdaa6b 100644 --- a/db/rocksdb.go +++ b/db/rocksdb.go @@ -1740,6 +1740,11 @@ func (d *RocksDB) SetInternalState(is *common.InternalState) { d.is = is } +// GetInternalState gets the InternalState +func (d *RocksDB) GetInternalState() *common.InternalState { + return d.is +} + // StoreInternalState stores the internal state to db func (d *RocksDB) StoreInternalState(is *common.InternalState) error { if d.metrics != nil { diff --git a/fiat/fiat_rates.go b/fiat/fiat_rates.go index 37e0d835..f7a4ac36 100644 --- a/fiat/fiat_rates.go +++ b/fiat/fiat_rates.go @@ -28,6 +28,7 @@ type RatesDownloader struct { timeFormat string callbackOnNewTicker OnNewFiatRatesTicker downloader RatesDownloaderInterface + downloadTokens bool } // NewFiatRatesDownloader initializes the downloader for FiatRates API. @@ -55,6 +56,8 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb } rd.db = db rd.callbackOnNewTicker = callback + rd.downloadTokens = rdParams.PlatformIdentifier != "" && rdParams.PlatformVsCurrency != "" + is := rd.db.GetInternalState() if apiType == "coingecko" { throttle := true if callback == nil { @@ -62,6 +65,11 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb throttle = false } rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, rd.timeFormat, throttle) + if is != nil { + is.HasFiatRates = true + is.HasTokenFiatRates = rd.downloadTokens + } + } else { return nil, fmt.Errorf("NewFiatRatesDownloader: incorrect API type %q", apiType) } @@ -71,6 +79,7 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, callb // Run periodically downloads current (every 15 minutes) and historical (once a day) tickers func (rd *RatesDownloader) Run() error { var lastHistoricalTickers time.Time + is := rd.db.GetInternalState() for { tickers, err := rd.downloader.CurrentTickers() @@ -79,6 +88,9 @@ func (rd *RatesDownloader) Run() error { } else { rd.db.FiatRatesSetCurrentTicker(tickers) glog.Info("FiatRatesDownloader: CurrentTickers updated") + if is != nil { + is.CurrentFiatRatesTime = time.Now() + } if rd.callbackOnNewTicker != nil { rd.callbackOnNewTicker(tickers) } @@ -94,16 +106,24 @@ func (rd *RatesDownloader) Run() error { glog.Error("FiatRatesDownloader: FiatRatesFindLastTicker error ", err) } else { glog.Infof("FiatRatesDownloader: UpdateHistoricalTickers finished, last ticker from %v", ticker.Timestamp) - } - // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there may be many tokens - go func() { - err := rd.downloader.UpdateHistoricalTokenTickers() - if err != nil { - glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) - } else { - glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + if is != nil { + is.HistoricalFiatRatesTime = ticker.Timestamp } - }() + } + if rd.downloadTokens { + // UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there are many tokens + go func() { + err := rd.downloader.UpdateHistoricalTokenTickers() + if err != nil { + glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err) + } else { + glog.Info("FiatRatesDownloader: UpdateHistoricalTokenTickers finished") + if is != nil { + is.HistoricalTokenFiatRatesTime = time.Now() + } + } + }() + } } } // wait for the next run with a slight random value to avoid too many request at the same time diff --git a/server/public.go b/server/public.go index 4147d241..d2b168ac 100644 --- a/server/public.go +++ b/server/public.go @@ -520,10 +520,14 @@ func (s *PublicServer) parseTemplates() []*template.Template { } func formatUnixTime(ut int64) string { - return formatTime(time.Unix(ut, 0)) + t := time.Unix(ut, 0) + return formatTime(&t) } -func formatTime(t time.Time) string { +func formatTime(t *time.Time) string { + if t == nil { + return "" + } return t.Format(time.RFC1123) } diff --git a/static/templates/index.html b/static/templates/index.html index 77080622..77ef6fbd 100644 --- a/static/templates/index.html +++ b/static/templates/index.html @@ -47,6 +47,16 @@ Transactions in Mempool {{if .InternalExplorer}}{{$bb.MempoolSize}}{{else}}{{$bb.MempoolSize}}{{end}} + {{- if $bb.HasFiatRates -}} + + Current Fiat rates + {{formatTime $bb.CurrentFiatRatesTime}} + + + Historical Fiat rates + {{formatTime $bb.HistoricalFiatRatesTime}}{{if $bb.HasTokenFiatRates}}
tokens {{formatTime $bb.HistoricalTokenFiatRatesTime}}{{end}} + + {{- end -}} Size On Disk {{$bb.DbSize}}