Refactor fiat rates handler
This commit is contained in:
parent
ad50758984
commit
d856618607
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain/coins/eth"
|
"github.com/trezor/blockbook/bchain/coins/eth"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Worker is handle to api worker
|
// Worker is handle to api worker
|
||||||
@ -31,11 +32,12 @@ type Worker struct {
|
|||||||
useAddressAliases bool
|
useAddressAliases bool
|
||||||
mempool bchain.Mempool
|
mempool bchain.Mempool
|
||||||
is *common.InternalState
|
is *common.InternalState
|
||||||
|
fiatRates *fiat.FiatRates
|
||||||
metrics *common.Metrics
|
metrics *common.Metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWorker creates new api worker
|
// NewWorker creates new api worker
|
||||||
func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*Worker, error) {
|
func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*Worker, error) {
|
||||||
w := &Worker{
|
w := &Worker{
|
||||||
db: db,
|
db: db,
|
||||||
txCache: txCache,
|
txCache: txCache,
|
||||||
@ -45,6 +47,7 @@ func NewWorker(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool,
|
|||||||
useAddressAliases: chain.GetChainParser().UseAddressAliases(),
|
useAddressAliases: chain.GetChainParser().UseAddressAliases(),
|
||||||
mempool: mempool,
|
mempool: mempool,
|
||||||
is: is,
|
is: is,
|
||||||
|
fiatRates: fiatRates,
|
||||||
metrics: metrics,
|
metrics: metrics,
|
||||||
}
|
}
|
||||||
if w.chainType == bchain.ChainBitcoinType {
|
if w.chainType == bchain.ChainBitcoinType {
|
||||||
@ -1072,7 +1075,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
|
return nil, nil, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
|
||||||
}
|
}
|
||||||
ticker := w.is.GetCurrentTicker("", "")
|
ticker := w.fiatRates.GetCurrentTicker("", "")
|
||||||
if details > AccountDetailsBasic {
|
if details > AccountDetailsBasic {
|
||||||
d.tokens = make([]Token, len(ca.Contracts))
|
d.tokens = make([]Token, len(ca.Contracts))
|
||||||
var j int
|
var j int
|
||||||
@ -1346,7 +1349,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
|
|||||||
}
|
}
|
||||||
var secondaryRate, totalSecondaryValue, totalBaseValue, secondaryValue float64
|
var secondaryRate, totalSecondaryValue, totalBaseValue, secondaryValue float64
|
||||||
if secondaryCoin != "" {
|
if secondaryCoin != "" {
|
||||||
ticker := w.is.GetCurrentTicker("", "")
|
ticker := w.fiatRates.GetCurrentTicker("", "")
|
||||||
balance, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
|
balance, err := strconv.ParseFloat((*Amount)(&ba.BalanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
|
||||||
if ticker != nil && err == nil {
|
if ticker != nil && err == nil {
|
||||||
r, found := ticker.Rates[secondaryCoin]
|
r, found := ticker.Rates[secondaryCoin]
|
||||||
@ -1887,7 +1890,7 @@ func (w *Worker) GetCurrentFiatRates(currencies []string, token string) (*FiatTi
|
|||||||
if len(currencies) == 1 {
|
if len(currencies) == 1 {
|
||||||
vsCurrency = currencies[0]
|
vsCurrency = currencies[0]
|
||||||
}
|
}
|
||||||
ticker := w.is.GetCurrentTicker(vsCurrency, token)
|
ticker := w.fiatRates.GetCurrentTicker(vsCurrency, token)
|
||||||
var err error
|
var err error
|
||||||
if ticker == nil {
|
if ticker == nil {
|
||||||
ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token)
|
ticker, err = w.db.FiatRatesFindLastTicker(vsCurrency, token)
|
||||||
@ -2289,8 +2292,9 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
|
|||||||
internalDBSize = w.is.DBSizeTotal()
|
internalDBSize = w.is.DBSizeTotal()
|
||||||
}
|
}
|
||||||
var currentFiatRatesTime time.Time
|
var currentFiatRatesTime time.Time
|
||||||
if w.is.CurrentTicker != nil {
|
ct := w.fiatRates.GetCurrentTicker("", "")
|
||||||
currentFiatRatesTime = w.is.CurrentTicker.Timestamp
|
if ct != nil {
|
||||||
|
currentFiatRatesTime = ct.Timestamp
|
||||||
}
|
}
|
||||||
blockbookInfo := &BlockbookInfo{
|
blockbookInfo := &BlockbookInfo{
|
||||||
Coin: w.is.Coin,
|
Coin: w.is.Coin,
|
||||||
|
|||||||
@ -571,7 +571,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
|
|||||||
|
|
||||||
var secondaryValue float64
|
var secondaryValue float64
|
||||||
if secondaryCoin != "" {
|
if secondaryCoin != "" {
|
||||||
ticker := w.is.GetCurrentTicker("", "")
|
ticker := w.fiatRates.GetCurrentTicker("", "")
|
||||||
balance, err := strconv.ParseFloat((*Amount)(&data.balanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
|
balance, err := strconv.ParseFloat((*Amount)(&data.balanceSat).DecimalString(w.chainParser.AmountDecimals()), 64)
|
||||||
if ticker != nil && err == nil {
|
if ticker != nil && err == nil {
|
||||||
r, found := ticker.Rates[secondaryCoin]
|
r, found := ticker.Rates[secondaryCoin]
|
||||||
|
|||||||
35
blockbook.go
35
blockbook.go
@ -102,6 +102,7 @@ var (
|
|||||||
metrics *common.Metrics
|
metrics *common.Metrics
|
||||||
syncWorker *db.SyncWorker
|
syncWorker *db.SyncWorker
|
||||||
internalState *common.InternalState
|
internalState *common.InternalState
|
||||||
|
fiatRates *fiat.FiatRates
|
||||||
callbacksOnNewBlock []bchain.OnNewBlockFunc
|
callbacksOnNewBlock []bchain.OnNewBlockFunc
|
||||||
callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc
|
callbacksOnNewTxAddr []bchain.OnNewTxAddrFunc
|
||||||
callbacksOnNewTx []bchain.OnNewTxFunc
|
callbacksOnNewTx []bchain.OnNewTxFunc
|
||||||
@ -273,6 +274,11 @@ func mainWithExitCode() int {
|
|||||||
return exitCodeFatal
|
return exitCodeFatal
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if fiatRates, err = fiat.NewFiatRates(index, *configFile, onNewFiatRatesTicker); err != nil {
|
||||||
|
glog.Error("fiatRates ", err)
|
||||||
|
return exitCodeFatal
|
||||||
|
}
|
||||||
|
|
||||||
// report BlockbookAppInfo metric, only log possible error
|
// report BlockbookAppInfo metric, only log possible error
|
||||||
if err = blockbookAppInfoMetric(index, chain, txCache, internalState, metrics); err != nil {
|
if err = blockbookAppInfoMetric(index, chain, txCache, internalState, metrics); err != nil {
|
||||||
glog.Error("blockbookAppInfoMetric ", err)
|
glog.Error("blockbookAppInfoMetric ", err)
|
||||||
@ -397,7 +403,7 @@ func getBlockChainWithRetry(coin string, configFile string, pushHandler func(bch
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startInternalServer() (*server.InternalServer, error) {
|
func startInternalServer() (*server.InternalServer, error) {
|
||||||
internalServer, err := server.NewInternalServer(*internalBinding, *certFiles, index, chain, mempool, txCache, metrics, internalState)
|
internalServer, err := server.NewInternalServer(*internalBinding, *certFiles, index, chain, mempool, txCache, metrics, internalState, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -417,7 +423,7 @@ func startInternalServer() (*server.InternalServer, error) {
|
|||||||
|
|
||||||
func startPublicServer() (*server.PublicServer, error) {
|
func startPublicServer() (*server.PublicServer, error) {
|
||||||
// start public server in limited functionality, extend it after sync is finished by calling ConnectFullPublicInterface
|
// start public server in limited functionality, extend it after sync is finished by calling ConnectFullPublicInterface
|
||||||
publicServer, err := server.NewPublicServer(*publicBinding, *certFiles, index, chain, mempool, txCache, *explorerURL, metrics, internalState, *debugMode)
|
publicServer, err := server.NewPublicServer(*publicBinding, *certFiles, index, chain, mempool, txCache, *explorerURL, metrics, internalState, fiatRates, *debugMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -463,7 +469,7 @@ func performRollback() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
|
func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -682,7 +688,7 @@ func waitForSignalAndShutdown(internal *server.InternalServer, public *server.Pu
|
|||||||
func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
|
func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
glog.Info("computeFeeStats start")
|
glog.Info("computeFeeStats start")
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -692,6 +698,10 @@ func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string) {
|
func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string) {
|
||||||
|
if fiatRates.Enabled {
|
||||||
|
go fiatRates.RunDownloader()
|
||||||
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile(configFile)
|
data, err := ioutil.ReadFile(configFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Error reading file %v, %v", configFile, err)
|
glog.Errorf("Error reading file %v, %v", configFile, err)
|
||||||
@ -699,10 +709,7 @@ func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var config struct {
|
var config struct {
|
||||||
FiatRates string `json:"fiat_rates"`
|
FourByteSignatures string `json:"fourByteSignatures"`
|
||||||
FiatRatesParams string `json:"fiat_rates_params"`
|
|
||||||
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
|
|
||||||
FourByteSignatures string `json:"fourByteSignatures"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.Unmarshal(data, &config)
|
err = json.Unmarshal(data, &config)
|
||||||
@ -711,18 +718,6 @@ func initDownloaders(db *db.RocksDB, chain bchain.BlockChain, configFile string)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.FiatRates == "" || config.FiatRatesParams == "" {
|
|
||||||
glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configFile)
|
|
||||||
} else {
|
|
||||||
fiatRates, err := fiat.NewFiatRatesDownloader(db, config.FiatRates, config.FiatRatesParams, config.FiatRatesVsCurrencies, onNewFiatRatesTicker)
|
|
||||||
if err != nil {
|
|
||||||
glog.Errorf("NewFiatRatesDownloader Init error: %v", err)
|
|
||||||
} else {
|
|
||||||
glog.Infof("Starting %v FiatRates downloader...", config.FiatRates)
|
|
||||||
go fiatRates.Run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.FourByteSignatures != "" && chain.GetChainParser().GetChainType() == bchain.ChainEthereumType {
|
if config.FourByteSignatures != "" && chain.GetChainParser().GetChainType() == bchain.ChainEthereumType {
|
||||||
fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures)
|
fbsd, err := fourbyte.NewFourByteSignaturesDownloader(db, config.FourByteSignatures)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -7,9 +7,9 @@ import (
|
|||||||
|
|
||||||
// CurrencyRatesTicker contains coin ticker data fetched from API
|
// CurrencyRatesTicker contains coin ticker data fetched from API
|
||||||
type CurrencyRatesTicker struct {
|
type CurrencyRatesTicker struct {
|
||||||
Timestamp time.Time `json:"timestamp"` // return as unix timestamp in API
|
Timestamp time.Time `json:"timestamp"` // return as unix timestamp in API
|
||||||
Rates map[string]float32 `json:"rates"` // rates of the base currency against a list of vs currencies
|
Rates map[string]float32 `json:"rates"` // rates of the base currency against a list of vs currencies
|
||||||
TokenRates map[string]float32 `json:"tokenRates"` // rates of the tokens (identified by the address of the contract) against the base currency
|
TokenRates map[string]float32 `json:"tokenRates,omitempty"` // rates of the tokens (identified by the address of the contract) against the base currency
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@ -80,11 +80,10 @@ type InternalState struct {
|
|||||||
|
|
||||||
DbColumns []InternalStateColumn `json:"dbColumns"`
|
DbColumns []InternalStateColumn `json:"dbColumns"`
|
||||||
|
|
||||||
HasFiatRates bool `json:"-"`
|
HasFiatRates bool `json:"-"`
|
||||||
HasTokenFiatRates bool `json:"-"`
|
HasTokenFiatRates bool `json:"-"`
|
||||||
HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"`
|
HistoricalFiatRatesTime time.Time `json:"historicalFiatRatesTime"`
|
||||||
HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime"`
|
HistoricalTokenFiatRatesTime time.Time `json:"historicalTokenFiatRatesTime"`
|
||||||
CurrentTicker *CurrencyRatesTicker `json:"currentTicker"`
|
|
||||||
|
|
||||||
EnableSubNewTx bool `json:"-"`
|
EnableSubNewTx bool `json:"-"`
|
||||||
|
|
||||||
@ -319,24 +318,6 @@ func (is *InternalState) Pack() ([]byte, error) {
|
|||||||
return json.Marshal(is)
|
return json.Marshal(is)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCurrentTicker returns current ticker
|
|
||||||
func (is *InternalState) GetCurrentTicker(vsCurrency string, token string) *CurrencyRatesTicker {
|
|
||||||
is.mux.Lock()
|
|
||||||
currentTicker := is.CurrentTicker
|
|
||||||
is.mux.Unlock()
|
|
||||||
if currentTicker != nil && IsSuitableTicker(currentTicker, vsCurrency, token) {
|
|
||||||
return currentTicker
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCurrentTicker sets current ticker
|
|
||||||
func (is *InternalState) SetCurrentTicker(t *CurrencyRatesTicker) {
|
|
||||||
is.mux.Lock()
|
|
||||||
defer is.mux.Unlock()
|
|
||||||
is.CurrentTicker = t
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnpackInternalState unmarshals internal state from json
|
// UnpackInternalState unmarshals internal state from json
|
||||||
func UnpackInternalState(buf []byte) (*InternalState, error) {
|
func UnpackInternalState(buf []byte) (*InternalState, error) {
|
||||||
var is InternalState
|
var is InternalState
|
||||||
|
|||||||
66
db/fiat.go
66
db/fiat.go
@ -2,8 +2,8 @@ package db
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
vlq "github.com/bsm/go-vlq"
|
vlq "github.com/bsm/go-vlq"
|
||||||
@ -16,9 +16,6 @@ import (
|
|||||||
// FiatRatesTimeFormat is a format string for storing FiatRates timestamps in rocksdb
|
// FiatRatesTimeFormat is a format string for storing FiatRates timestamps in rocksdb
|
||||||
const FiatRatesTimeFormat = "20060102150405" // YYYYMMDDhhmmss
|
const FiatRatesTimeFormat = "20060102150405" // YYYYMMDDhhmmss
|
||||||
|
|
||||||
var lastTickerInDB *common.CurrencyRatesTicker
|
|
||||||
var lastTickerInDBMux sync.Mutex
|
|
||||||
|
|
||||||
func packTimestamp(t *time.Time) []byte {
|
func packTimestamp(t *time.Time) []byte {
|
||||||
return []byte(t.UTC().Format(FiatRatesTimeFormat))
|
return []byte(t.UTC().Format(FiatRatesTimeFormat))
|
||||||
}
|
}
|
||||||
@ -148,21 +145,21 @@ func (d *RocksDB) FiatRatesGetTicker(tickerTime *time.Time) (*common.CurrencyRat
|
|||||||
|
|
||||||
// FiatRatesFindTicker gets FiatRates data closest to the specified timestamp, of the base currency, vsCurrency or the token if specified
|
// FiatRatesFindTicker gets FiatRates data closest to the specified timestamp, of the base currency, vsCurrency or the token if specified
|
||||||
func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time, vsCurrency string, token string) (*common.CurrencyRatesTicker, error) {
|
func (d *RocksDB) FiatRatesFindTicker(tickerTime *time.Time, vsCurrency string, token string) (*common.CurrencyRatesTicker, error) {
|
||||||
currentTicker := d.is.GetCurrentTicker("", "")
|
// currentTicker := d.is.GetCurrentTicker("", "")
|
||||||
lastTickerInDBMux.Lock()
|
// lastTickerInDBMux.Lock()
|
||||||
dbTicker := lastTickerInDB
|
// dbTicker := lastTickerInDB
|
||||||
lastTickerInDBMux.Unlock()
|
// lastTickerInDBMux.Unlock()
|
||||||
if currentTicker != nil {
|
// if currentTicker != nil {
|
||||||
if !tickerTime.Before(currentTicker.Timestamp) || (dbTicker != nil && tickerTime.After(dbTicker.Timestamp)) {
|
// if !tickerTime.Before(currentTicker.Timestamp) || (dbTicker != nil && tickerTime.After(dbTicker.Timestamp)) {
|
||||||
f := true
|
// f := true
|
||||||
if token != "" && currentTicker.TokenRates != nil {
|
// if token != "" && currentTicker.TokenRates != nil {
|
||||||
_, f = currentTicker.TokenRates[token]
|
// _, f = currentTicker.TokenRates[token]
|
||||||
}
|
// }
|
||||||
if f {
|
// if f {
|
||||||
return currentTicker, nil
|
// return currentTicker, nil
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
tickerTimeFormatted := tickerTime.UTC().Format(FiatRatesTimeFormat)
|
tickerTimeFormatted := tickerTime.UTC().Format(FiatRatesTimeFormat)
|
||||||
it := d.db.NewIteratorCF(d.ro, d.cfh[cfFiatRates])
|
it := d.db.NewIteratorCF(d.ro, d.cfh[cfFiatRates])
|
||||||
@ -193,14 +190,33 @@ func (d *RocksDB) FiatRatesFindLastTicker(vsCurrency string, token string) (*com
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if ticker != nil {
|
if ticker != nil {
|
||||||
// if without filter, store the ticker for later use
|
|
||||||
if vsCurrency == "" && token == "" {
|
|
||||||
lastTickerInDBMux.Lock()
|
|
||||||
lastTickerInDB = ticker
|
|
||||||
lastTickerInDBMux.Unlock()
|
|
||||||
}
|
|
||||||
return ticker, nil
|
return ticker, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *RocksDB) FiatRatesGetSpecialTickers(key string) (*[]common.CurrencyRatesTicker, error) {
|
||||||
|
val, err := d.db.GetCF(d.ro, d.cfh[cfDefault], []byte(key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer val.Free()
|
||||||
|
data := val.Data()
|
||||||
|
if data == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var tickers []common.CurrencyRatesTicker
|
||||||
|
if err := json.Unmarshal(data, &tickers); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &tickers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *RocksDB) FiatRatesStoreSpecialTickers(key string, tickers *[]common.CurrencyRatesTicker) error {
|
||||||
|
data, err := json.Marshal(tickers)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return d.db.PutCF(d.wo, d.cfh[cfDefault], []byte(key), data)
|
||||||
|
}
|
||||||
|
|||||||
@ -158,22 +158,6 @@ func TestRocksTickers(t *testing.T) {
|
|||||||
t.Errorf("Ticker %v found unexpectedly for aud vsCurrency", ticker)
|
t.Errorf("Ticker %v found unexpectedly for aud vsCurrency", ticker)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker = d.is.GetCurrentTicker("", "")
|
|
||||||
if ticker != nil {
|
|
||||||
t.Errorf("FiatRatesGetCurrentTicker %v found unexpectedly", ticker)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.is.SetCurrentTicker(ticker1)
|
|
||||||
ticker = d.is.GetCurrentTicker("", "")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("TestRocksTickers err: %+v", err)
|
|
||||||
} else if ticker == nil {
|
|
||||||
t.Errorf("Ticker not found")
|
|
||||||
} else if ticker.Timestamp.Format(FiatRatesTimeFormat) != ticker1.Timestamp.Format(FiatRatesTimeFormat) {
|
|
||||||
t.Errorf("Incorrect ticker found. Expected: %v, found: %+v", ticker1.Timestamp, ticker.Timestamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
d.is.SetCurrentTicker(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_packUnpackCurrencyRatesTicker(t *testing.T) {
|
func Test_packUnpackCurrencyRatesTicker(t *testing.T) {
|
||||||
|
|||||||
@ -190,13 +190,15 @@ func (cg *Coingecko) coinsList() (coinList, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// coinMarketChart /coins/{id}/market_chart?vs_currency={usd, eur, jpy, etc.}&days={1,14,30,max}
|
// coinMarketChart /coins/{id}/market_chart?vs_currency={usd, eur, jpy, etc.}&days={1,14,30,max}
|
||||||
func (cg *Coingecko) coinMarketChart(id string, vs_currency string, days string) (*marketChartPrices, error) {
|
func (cg *Coingecko) coinMarketChart(id string, vs_currency string, days string, daily bool) (*marketChartPrices, error) {
|
||||||
if len(id) == 0 || len(vs_currency) == 0 || len(days) == 0 {
|
if len(id) == 0 || len(vs_currency) == 0 || len(days) == 0 {
|
||||||
return nil, fmt.Errorf("id, vs_currency, and days is required")
|
return nil, fmt.Errorf("id, vs_currency, and days is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("interval", "daily")
|
if daily {
|
||||||
|
params.Add("interval", "daily")
|
||||||
|
}
|
||||||
params.Add("vs_currency", vs_currency)
|
params.Add("vs_currency", vs_currency)
|
||||||
params.Add("days", days)
|
params.Add("days", days)
|
||||||
|
|
||||||
@ -241,6 +243,7 @@ func (cg *Coingecko) platformIds() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentTickers returns the latest exchange rates
|
||||||
func (cg *Coingecko) CurrentTickers() (*common.CurrencyRatesTicker, error) {
|
func (cg *Coingecko) CurrentTickers() (*common.CurrencyRatesTicker, error) {
|
||||||
cg.updatingCurrent = true
|
cg.updatingCurrent = true
|
||||||
defer func() { cg.updatingCurrent = false }()
|
defer func() { cg.updatingCurrent = false }()
|
||||||
@ -296,6 +299,16 @@ func (cg *Coingecko) CurrentTickers() (*common.CurrencyRatesTicker, error) {
|
|||||||
return &newTickers, nil
|
return &newTickers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HourlyTickers returns the array of the exchange rates in hourly granularity
|
||||||
|
func (cg *Coingecko) HourlyTickers() (*[]common.CurrencyRatesTicker, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HourlyTickers returns the array of the exchange rates in five minutes granularity
|
||||||
|
func (cg *Coingecko) FiveMinutesTickers() (*[]common.CurrencyRatesTicker, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*common.CurrencyRatesTicker, coinId string, vsCurrency string, token string) (bool, error) {
|
func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*common.CurrencyRatesTicker, coinId string, vsCurrency string, token string) (bool, error) {
|
||||||
lastTicker, err := cg.db.FiatRatesFindLastTicker(vsCurrency, token)
|
lastTicker, err := cg.db.FiatRatesFindLastTicker(vsCurrency, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -312,7 +325,7 @@ func (cg *Coingecko) getHistoricalTicker(tickersToUpdate map[uint]*common.Curren
|
|||||||
}
|
}
|
||||||
days = strconv.Itoa(d)
|
days = strconv.Itoa(d)
|
||||||
}
|
}
|
||||||
mc, err := cg.coinMarketChart(coinId, vsCurrency, days)
|
mc, err := cg.coinMarketChart(coinId, vsCurrency, days, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,8 +4,10 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@ -13,29 +15,84 @@ import (
|
|||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const CurrentTickersKey = "CurrentTickers"
|
||||||
|
const HourlyTickersKey = "HourlyTickers"
|
||||||
|
const FiveMinutesTickersKey = "FiveMinutesTickers"
|
||||||
|
|
||||||
// OnNewFiatRatesTicker is used to send notification about a new FiatRates ticker
|
// OnNewFiatRatesTicker is used to send notification about a new FiatRates ticker
|
||||||
type OnNewFiatRatesTicker func(ticker *common.CurrencyRatesTicker)
|
type OnNewFiatRatesTicker func(ticker *common.CurrencyRatesTicker)
|
||||||
|
|
||||||
// RatesDownloaderInterface provides method signatures for specific fiat rates downloaders
|
// RatesDownloaderInterface provides method signatures for specific fiat rates downloaders
|
||||||
type RatesDownloaderInterface interface {
|
type RatesDownloaderInterface interface {
|
||||||
CurrentTickers() (*common.CurrencyRatesTicker, error)
|
CurrentTickers() (*common.CurrencyRatesTicker, error)
|
||||||
|
HourlyTickers() (*[]common.CurrencyRatesTicker, error)
|
||||||
|
FiveMinutesTickers() (*[]common.CurrencyRatesTicker, error)
|
||||||
UpdateHistoricalTickers() error
|
UpdateHistoricalTickers() error
|
||||||
UpdateHistoricalTokenTickers() error
|
UpdateHistoricalTokenTickers() error
|
||||||
}
|
}
|
||||||
|
|
||||||
// RatesDownloader stores FiatRates API parameters
|
// FiatRates stores FiatRates API parameters
|
||||||
type RatesDownloader struct {
|
type FiatRates struct {
|
||||||
periodSeconds int64
|
Enabled bool
|
||||||
db *db.RocksDB
|
periodSeconds int64
|
||||||
timeFormat string
|
db *db.RocksDB
|
||||||
callbackOnNewTicker OnNewFiatRatesTicker
|
timeFormat string
|
||||||
downloader RatesDownloaderInterface
|
callbackOnNewTicker OnNewFiatRatesTicker
|
||||||
downloadTokens bool
|
downloader RatesDownloaderInterface
|
||||||
|
downloadTokens bool
|
||||||
|
provider string
|
||||||
|
allowedVsCurrencies string
|
||||||
|
mux sync.RWMutex
|
||||||
|
currentTicker *common.CurrencyRatesTicker
|
||||||
|
hourlyTickers map[int64]*common.CurrencyRatesTicker
|
||||||
|
hourlyTickersFrom int64
|
||||||
|
hourlyTickersTo int64
|
||||||
|
fiveMinutesTickers map[int64]*common.CurrencyRatesTicker
|
||||||
|
fiveMinutesTickersFrom int64
|
||||||
|
fiveMinutesTickersTo int64
|
||||||
|
dailyTickers map[int64]*common.CurrencyRatesTicker
|
||||||
|
dailyTickersFrom int64
|
||||||
|
dailyTickersTo int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFiatRatesDownloader initializes the downloader for FiatRates API.
|
func tickersToMap(tickers *[]common.CurrencyRatesTicker, granularitySeconds int64) (map[int64]*common.CurrencyRatesTicker, int64, int64) {
|
||||||
func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, allowedVsCurrencies string, callback OnNewFiatRatesTicker) (*RatesDownloader, error) {
|
if tickers == nil || len(*tickers) == 0 {
|
||||||
var rd = &RatesDownloader{}
|
return nil, 0, 0
|
||||||
|
}
|
||||||
|
halfGranularity := granularitySeconds / 2
|
||||||
|
m := make(map[int64]*common.CurrencyRatesTicker, len(*tickers))
|
||||||
|
from := ((*tickers)[0].Timestamp.UTC().Unix() + halfGranularity) % granularitySeconds
|
||||||
|
to := ((*tickers)[len(*tickers)-1].Timestamp.UTC().Unix() + halfGranularity) % granularitySeconds
|
||||||
|
return m, from, to
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFiatRates initializes the FiatRates handler
|
||||||
|
func NewFiatRates(db *db.RocksDB, configFile string, callback OnNewFiatRatesTicker) (*FiatRates, error) {
|
||||||
|
var config struct {
|
||||||
|
FiatRates string `json:"fiat_rates"`
|
||||||
|
FiatRatesParams string `json:"fiat_rates_params"`
|
||||||
|
FiatRatesVsCurrencies string `json:"fiat_rates_vs_currencies"`
|
||||||
|
}
|
||||||
|
data, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading file %v, %v", configFile, err)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(data, &config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error parsing config file %v, %v", configFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fr = &FiatRates{
|
||||||
|
provider: config.FiatRates,
|
||||||
|
allowedVsCurrencies: config.FiatRatesVsCurrencies,
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.FiatRates == "" || config.FiatRatesParams == "" {
|
||||||
|
glog.Infof("FiatRates config (%v) is empty, not downloading fiat rates", configFile)
|
||||||
|
fr.Enabled = false
|
||||||
|
return fr, nil
|
||||||
|
}
|
||||||
|
|
||||||
type fiatRatesParams struct {
|
type fiatRatesParams struct {
|
||||||
URL string `json:"url"`
|
URL string `json:"url"`
|
||||||
Coin string `json:"coin"`
|
Coin string `json:"coin"`
|
||||||
@ -44,80 +101,151 @@ func NewFiatRatesDownloader(db *db.RocksDB, apiType string, params string, allow
|
|||||||
PeriodSeconds int64 `json:"periodSeconds"`
|
PeriodSeconds int64 `json:"periodSeconds"`
|
||||||
}
|
}
|
||||||
rdParams := &fiatRatesParams{}
|
rdParams := &fiatRatesParams{}
|
||||||
err := json.Unmarshal([]byte(params), &rdParams)
|
err = json.Unmarshal([]byte(config.FiatRatesParams), &rdParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if rdParams.URL == "" || rdParams.PeriodSeconds == 0 {
|
if rdParams.URL == "" || rdParams.PeriodSeconds == 0 {
|
||||||
return nil, errors.New("Missing parameters")
|
return nil, errors.New("missing parameters")
|
||||||
}
|
}
|
||||||
rd.timeFormat = "02-01-2006" // Layout string for FiatRates date formatting (DD-MM-YYYY)
|
fr.timeFormat = "02-01-2006" // Layout string for FiatRates date formatting (DD-MM-YYYY)
|
||||||
rd.periodSeconds = rdParams.PeriodSeconds // Time period for syncing the latest market data
|
fr.periodSeconds = rdParams.PeriodSeconds // Time period for syncing the latest market data
|
||||||
if rd.periodSeconds < 60 { // minimum is one minute
|
if fr.periodSeconds < 60 { // minimum is one minute
|
||||||
rd.periodSeconds = 60
|
fr.periodSeconds = 60
|
||||||
}
|
}
|
||||||
rd.db = db
|
fr.db = db
|
||||||
rd.callbackOnNewTicker = callback
|
fr.callbackOnNewTicker = callback
|
||||||
rd.downloadTokens = rdParams.PlatformIdentifier != "" && rdParams.PlatformVsCurrency != ""
|
fr.downloadTokens = rdParams.PlatformIdentifier != "" && rdParams.PlatformVsCurrency != ""
|
||||||
if rd.downloadTokens {
|
if fr.downloadTokens {
|
||||||
common.TickerRecalculateTokenRate = strings.ToLower(db.GetInternalState().CoinShortcut) != rdParams.PlatformVsCurrency
|
common.TickerRecalculateTokenRate = strings.ToLower(db.GetInternalState().CoinShortcut) != rdParams.PlatformVsCurrency
|
||||||
common.TickerTokenVsCurrency = rdParams.PlatformVsCurrency
|
common.TickerTokenVsCurrency = rdParams.PlatformVsCurrency
|
||||||
}
|
}
|
||||||
is := rd.db.GetInternalState()
|
is := fr.db.GetInternalState()
|
||||||
if apiType == "coingecko" {
|
if fr.provider == "coingecko" {
|
||||||
throttle := true
|
throttle := true
|
||||||
if callback == nil {
|
if callback == nil {
|
||||||
// a small hack - in tests the callback is not used, therefore there is no delay slowing down the test
|
// a small hack - in tests the callback is not used, therefore there is no delay slowing down the test
|
||||||
throttle = false
|
throttle = false
|
||||||
}
|
}
|
||||||
rd.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, allowedVsCurrencies, rd.timeFormat, throttle)
|
fr.downloader = NewCoinGeckoDownloader(db, rdParams.URL, rdParams.Coin, rdParams.PlatformIdentifier, rdParams.PlatformVsCurrency, fr.allowedVsCurrencies, fr.timeFormat, throttle)
|
||||||
if is != nil {
|
if is != nil {
|
||||||
is.HasFiatRates = true
|
is.HasFiatRates = true
|
||||||
is.HasTokenFiatRates = rd.downloadTokens
|
is.HasTokenFiatRates = fr.downloadTokens
|
||||||
}
|
fr.Enabled = true
|
||||||
|
|
||||||
|
currentTickers, err := db.FiatRatesGetSpecialTickers(CurrentTickersKey)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("FiatRatesDownloader: get CurrentTickers from DB error ", err)
|
||||||
|
}
|
||||||
|
if currentTickers != nil && len(*currentTickers) > 0 {
|
||||||
|
fr.currentTicker = &(*currentTickers)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
hourlyTickers, err := db.FiatRatesGetSpecialTickers(HourlyTickersKey)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("FiatRatesDownloader: get HourlyTickers from DB error ", err)
|
||||||
|
}
|
||||||
|
fr.hourlyTickers, fr.hourlyTickersFrom, fr.hourlyTickersTo = tickersToMap(hourlyTickers, 3600)
|
||||||
|
|
||||||
|
fiveMinutesTickers, err := db.FiatRatesGetSpecialTickers(FiveMinutesTickersKey)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error("FiatRatesDownloader: get FiveMinutesTickers from DB error ", err)
|
||||||
|
}
|
||||||
|
fr.fiveMinutesTickers, fr.fiveMinutesTickersFrom, fr.fiveMinutesTickersTo = tickersToMap(fiveMinutesTickers, 5*60)
|
||||||
|
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("NewFiatRatesDownloader: incorrect API type %q", apiType)
|
return nil, fmt.Errorf("unknown provider %q", fr.provider)
|
||||||
}
|
}
|
||||||
return rd, nil
|
return fr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run periodically downloads current (every 15 minutes) and historical (once a day) tickers
|
// GetCurrentTicker returns current ticker
|
||||||
func (rd *RatesDownloader) Run() error {
|
func (fr *FiatRates) GetCurrentTicker(vsCurrency string, token string) *common.CurrencyRatesTicker {
|
||||||
|
fr.mux.RLock()
|
||||||
|
currentTicker := fr.currentTicker
|
||||||
|
fr.mux.RUnlock()
|
||||||
|
if currentTicker != nil && common.IsSuitableTicker(currentTicker, vsCurrency, token) {
|
||||||
|
return currentTicker
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCurrentTicker sets current ticker
|
||||||
|
func (fr *FiatRates) setCurrentTicker(t *common.CurrencyRatesTicker) {
|
||||||
|
fr.mux.Lock()
|
||||||
|
defer fr.mux.Unlock()
|
||||||
|
fr.currentTicker = t
|
||||||
|
fr.db.FiatRatesStoreSpecialTickers(CurrentTickersKey, &[]common.CurrencyRatesTicker{*t})
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCurrentTicker sets hourly tickers
|
||||||
|
func (fr *FiatRates) setHourlyTickers(t *[]common.CurrencyRatesTicker) {
|
||||||
|
fr.mux.Lock()
|
||||||
|
defer fr.mux.Unlock()
|
||||||
|
fr.hourlyTickers, fr.hourlyTickersFrom, fr.hourlyTickersTo = tickersToMap(t, 3600)
|
||||||
|
fr.db.FiatRatesStoreSpecialTickers(HourlyTickersKey, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCurrentTicker sets hourly tickers
|
||||||
|
func (fr *FiatRates) setFiveMinutesTickers(t *[]common.CurrencyRatesTicker) {
|
||||||
|
fr.mux.Lock()
|
||||||
|
defer fr.mux.Unlock()
|
||||||
|
fr.fiveMinutesTickers, fr.fiveMinutesTickersFrom, fr.fiveMinutesTickersTo = tickersToMap(t, 5*60)
|
||||||
|
fr.db.FiatRatesStoreSpecialTickers(FiveMinutesTickersKey, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunDownloader periodically downloads current (every 15 minutes) and historical (once a day) tickers
|
||||||
|
func (fr *FiatRates) RunDownloader() error {
|
||||||
|
glog.Infof("Starting %v FiatRates downloader...", fr.provider)
|
||||||
var lastHistoricalTickers time.Time
|
var lastHistoricalTickers time.Time
|
||||||
is := rd.db.GetInternalState()
|
is := fr.db.GetInternalState()
|
||||||
tickerFromIs := is.GetCurrentTicker("", "")
|
tickerFromIs := fr.GetCurrentTicker("", "")
|
||||||
firstRun := true
|
firstRun := true
|
||||||
for {
|
for {
|
||||||
unix := time.Now().Unix()
|
unix := time.Now().Unix()
|
||||||
next := unix + rd.periodSeconds
|
next := unix + fr.periodSeconds
|
||||||
next -= next % rd.periodSeconds
|
next -= next % fr.periodSeconds
|
||||||
// skip waiting for the period for the first run if there are no tickerFromIs or they are too old
|
// skip waiting for the period for the first run if there are no tickerFromIs or they are too old
|
||||||
if !firstRun || (tickerFromIs != nil && next-tickerFromIs.Timestamp.Unix() < rd.periodSeconds) {
|
if !firstRun || (tickerFromIs != nil && next-tickerFromIs.Timestamp.Unix() < fr.periodSeconds) {
|
||||||
// wait for the next run with a slight random value to avoid too many request at the same time
|
// wait for the next run with a slight random value to avoid too many request at the same time
|
||||||
next += int64(rand.Intn(12))
|
next += int64(rand.Intn(12))
|
||||||
time.Sleep(time.Duration(next-unix) * time.Second)
|
time.Sleep(time.Duration(next-unix) * time.Second)
|
||||||
}
|
}
|
||||||
firstRun = false
|
firstRun = false
|
||||||
tickers, err := rd.downloader.CurrentTickers()
|
currentTicker, err := fr.downloader.CurrentTickers()
|
||||||
if err != nil || tickers == nil {
|
if err != nil || currentTicker == nil {
|
||||||
glog.Error("FiatRatesDownloader: CurrentTickers error ", err)
|
glog.Error("FiatRatesDownloader: CurrentTickers error ", err)
|
||||||
} else {
|
} else {
|
||||||
is.SetCurrentTicker(tickers)
|
fr.setCurrentTicker(currentTicker)
|
||||||
glog.Info("FiatRatesDownloader: CurrentTickers updated")
|
glog.Info("FiatRatesDownloader: CurrentTickers updated")
|
||||||
if rd.callbackOnNewTicker != nil {
|
if fr.callbackOnNewTicker != nil {
|
||||||
rd.callbackOnNewTicker(tickers)
|
fr.callbackOnNewTicker(currentTicker)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hourlyTickers, err := fr.downloader.HourlyTickers()
|
||||||
|
if err != nil || hourlyTickers == nil {
|
||||||
|
glog.Error("FiatRatesDownloader: HourlyTickers error ", err)
|
||||||
|
} else {
|
||||||
|
fr.setHourlyTickers(hourlyTickers)
|
||||||
|
glog.Info("FiatRatesDownloader: HourlyTickers updated")
|
||||||
|
}
|
||||||
|
fiveMinutesTickers, err := fr.downloader.FiveMinutesTickers()
|
||||||
|
if err != nil || fiveMinutesTickers == nil {
|
||||||
|
glog.Error("FiatRatesDownloader: FiveMinutesTickers error ", err)
|
||||||
|
} else {
|
||||||
|
fr.setFiveMinutesTickers(fiveMinutesTickers)
|
||||||
|
glog.Info("FiatRatesDownloader: FiveMinutesTickers updated")
|
||||||
|
}
|
||||||
now := time.Now().UTC()
|
now := time.Now().UTC()
|
||||||
// once a day, 1 hour after UTC midnight (to let the provider prepare historical rates) update historical tickers
|
// once a day, 1 hour after UTC midnight (to let the provider prepare historical rates) update historical tickers
|
||||||
if (now.YearDay() != lastHistoricalTickers.YearDay() || now.Year() != lastHistoricalTickers.Year()) && now.Hour() > 0 {
|
if (now.YearDay() != lastHistoricalTickers.YearDay() || now.Year() != lastHistoricalTickers.Year()) && now.Hour() > 0 {
|
||||||
err = rd.downloader.UpdateHistoricalTickers()
|
err = fr.downloader.UpdateHistoricalTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("FiatRatesDownloader: UpdateHistoricalTickers error ", err)
|
glog.Error("FiatRatesDownloader: UpdateHistoricalTickers error ", err)
|
||||||
} else {
|
} else {
|
||||||
lastHistoricalTickers = time.Now().UTC()
|
lastHistoricalTickers = time.Now().UTC()
|
||||||
ticker, err := rd.db.FiatRatesFindLastTicker("", "")
|
ticker, err := fr.db.FiatRatesFindLastTicker("", "")
|
||||||
if err != nil || ticker == nil {
|
if err != nil || ticker == nil {
|
||||||
glog.Error("FiatRatesDownloader: FiatRatesFindLastTicker error ", err)
|
glog.Error("FiatRatesDownloader: FiatRatesFindLastTicker error ", err)
|
||||||
} else {
|
} else {
|
||||||
@ -126,10 +254,10 @@ func (rd *RatesDownloader) Run() error {
|
|||||||
is.HistoricalFiatRatesTime = ticker.Timestamp
|
is.HistoricalFiatRatesTime = ticker.Timestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rd.downloadTokens {
|
if fr.downloadTokens {
|
||||||
// UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there are many tokens
|
// UpdateHistoricalTokenTickers in a goroutine, it can take quite some time as there are many tokens
|
||||||
go func() {
|
go func() {
|
||||||
err := rd.downloader.UpdateHistoricalTokenTickers()
|
err := fr.downloader.UpdateHistoricalTokenTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err)
|
glog.Error("FiatRatesDownloader: UpdateHistoricalTokenTickers error ", err)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -3,9 +3,8 @@
|
|||||||
package fiat
|
package fiat
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
@ -32,7 +31,7 @@ func TestMain(m *testing.M) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) {
|
func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) {
|
||||||
tmp, err := ioutil.TempDir("", "testdb")
|
tmp, err := os.MkdirTemp("", "testdb")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -75,7 +74,7 @@ func getFiatRatesMockData(name string) (string, error) {
|
|||||||
glog.Errorf("Cannot open file %v", filename)
|
glog.Errorf("Cannot open file %v", filename)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
b, err := ioutil.ReadAll(mockFile)
|
b, err := io.ReadAll(mockFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Cannot read file %v", filename)
|
glog.Errorf("Cannot read file %v", filename)
|
||||||
return "", err
|
return "", err
|
||||||
@ -133,165 +132,159 @@ func TestFiatRates(t *testing.T) {
|
|||||||
// mocked CoinGecko API
|
// mocked CoinGecko API
|
||||||
configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}`
|
configJSON := `{"fiat_rates": "coingecko", "fiat_rates_params": "{\"url\": \"` + mockServer.URL + `\", \"coin\": \"ethereum\",\"platformIdentifier\":\"ethereum\",\"platformVsCurrency\": \"eth\",\"periodSeconds\": 60}"}`
|
||||||
|
|
||||||
type fiatRatesConfig struct {
|
configFile, err := os.CreateTemp("", "config*.json")
|
||||||
FiatRates string `json:"fiat_rates"`
|
|
||||||
FiatRatesParams string `json:"fiat_rates_params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var config fiatRatesConfig
|
|
||||||
err := json.Unmarshal([]byte(configJSON), &config)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error parsing config: %v", err)
|
t.Fatalf("FiatRates configFile error: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := configFile.WriteString(configJSON); err != nil {
|
||||||
|
t.Fatalf("FiatRates configFile WriteString error: %v", err)
|
||||||
|
}
|
||||||
|
if err := configFile.Close(); err != nil {
|
||||||
|
t.Fatalf("FiatRates configFile Close error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.FiatRates == "" || config.FiatRatesParams == "" {
|
fiatRates, err := NewFiatRates(d, configFile.Name(), nil)
|
||||||
t.Fatalf("Error parsing FiatRates config - empty parameter")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fiatRates, err := NewFiatRatesDownloader(d, config.FiatRates, config.FiatRatesParams, "", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("FiatRates init error: %v", err)
|
t.Fatalf("FiatRates init error: %v", err)
|
||||||
}
|
}
|
||||||
if config.FiatRates == "coingecko" {
|
|
||||||
|
|
||||||
// get current tickers
|
// get current tickers
|
||||||
currentTickers, err := fiatRates.downloader.CurrentTickers()
|
currentTickers, err := fiatRates.downloader.CurrentTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Error in CurrentTickers: %v", err)
|
t.Fatalf("Error in CurrentTickers: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if currentTickers == nil {
|
if currentTickers == nil {
|
||||||
t.Fatalf("CurrentTickers returned nil value")
|
t.Fatalf("CurrentTickers returned nil value")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wantCurrentTickers := common.CurrencyRatesTicker{
|
wantCurrentTickers := common.CurrencyRatesTicker{
|
||||||
Rates: map[string]float32{
|
Rates: map[string]float32{
|
||||||
"aed": 8447.1,
|
"aed": 8447.1,
|
||||||
"ars": 268901,
|
"ars": 268901,
|
||||||
"aud": 3314.36,
|
"aud": 3314.36,
|
||||||
"btc": 0.07531005,
|
"btc": 0.07531005,
|
||||||
"eth": 1,
|
"eth": 1,
|
||||||
"eur": 2182.99,
|
"eur": 2182.99,
|
||||||
"ltc": 29.097696,
|
"ltc": 29.097696,
|
||||||
"usd": 2299.72,
|
"usd": 2299.72,
|
||||||
},
|
},
|
||||||
TokenRates: map[string]float32{
|
TokenRates: map[string]float32{
|
||||||
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07,
|
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 5.58195e-07,
|
||||||
"0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10,
|
"0x906710835d1ae85275eb770f06873340ca54274b": 1.39852e-10,
|
||||||
},
|
},
|
||||||
Timestamp: currentTickers.Timestamp,
|
Timestamp: currentTickers.Timestamp,
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) {
|
if !reflect.DeepEqual(currentTickers, &wantCurrentTickers) {
|
||||||
t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers)
|
t.Fatalf("CurrentTickers() = %v, want %v", *currentTickers, wantCurrentTickers)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
ticker, err := fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
||||||
}
|
}
|
||||||
if ticker != nil {
|
if ticker != nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker found unexpected data")
|
t.Fatalf("FiatRatesFindLastTicker found unexpected data")
|
||||||
}
|
}
|
||||||
|
|
||||||
// update historical tickers for the first time
|
// update historical tickers for the first time
|
||||||
err = fiatRates.downloader.UpdateHistoricalTickers()
|
err = fiatRates.downloader.UpdateHistoricalTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err)
|
t.Fatalf("UpdateHistoricalTickers 1st pass failed with error: %v", err)
|
||||||
}
|
}
|
||||||
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
|
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err)
|
t.Fatalf("UpdateHistoricalTokenTickers 1st pass failed with error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
||||||
if err != nil || ticker == nil {
|
if err != nil || ticker == nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
||||||
}
|
}
|
||||||
wantTicker := common.CurrencyRatesTicker{
|
wantTicker := common.CurrencyRatesTicker{
|
||||||
Rates: map[string]float32{
|
Rates: map[string]float32{
|
||||||
"aed": 241272.48,
|
"aed": 241272.48,
|
||||||
"ars": 241272.48,
|
"ars": 241272.48,
|
||||||
"aud": 241272.48,
|
"aud": 241272.48,
|
||||||
"btc": 241272.48,
|
"btc": 241272.48,
|
||||||
"eth": 241272.48,
|
"eth": 241272.48,
|
||||||
"eur": 241272.48,
|
"eur": 241272.48,
|
||||||
"ltc": 241272.48,
|
"ltc": 241272.48,
|
||||||
"usd": 1794.5397,
|
"usd": 1794.5397,
|
||||||
},
|
},
|
||||||
TokenRates: map[string]float32{
|
TokenRates: map[string]float32{
|
||||||
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07,
|
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.161734e+07,
|
||||||
"0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07,
|
"0x906710835d1ae85275eb770f06873340ca54274b": 4.161734e+07,
|
||||||
},
|
},
|
||||||
Timestamp: time.Unix(1654732800, 0).UTC(),
|
Timestamp: time.Unix(1654732800, 0).UTC(),
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(ticker, &wantTicker) {
|
if !reflect.DeepEqual(ticker, &wantTicker) {
|
||||||
t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker)
|
t.Fatalf("UpdateHistoricalTickers(usd) 1st pass = %v, want %v", *ticker, wantTicker)
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
|
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
|
||||||
if err != nil || ticker == nil {
|
if err != nil || ticker == nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
||||||
}
|
}
|
||||||
wantTicker = common.CurrencyRatesTicker{
|
wantTicker = common.CurrencyRatesTicker{
|
||||||
Rates: map[string]float32{
|
Rates: map[string]float32{
|
||||||
"aed": 240402.97,
|
"aed": 240402.97,
|
||||||
"ars": 240402.97,
|
"ars": 240402.97,
|
||||||
"aud": 240402.97,
|
"aud": 240402.97,
|
||||||
"btc": 240402.97,
|
"btc": 240402.97,
|
||||||
"eth": 240402.97,
|
"eth": 240402.97,
|
||||||
"eur": 240402.97,
|
"eur": 240402.97,
|
||||||
"ltc": 240402.97,
|
"ltc": 240402.97,
|
||||||
},
|
},
|
||||||
TokenRates: map[string]float32{
|
TokenRates: map[string]float32{
|
||||||
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
|
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
|
||||||
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
|
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
|
||||||
},
|
},
|
||||||
Timestamp: time.Unix(1654819200, 0).UTC(),
|
Timestamp: time.Unix(1654819200, 0).UTC(),
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(ticker, &wantTicker) {
|
if !reflect.DeepEqual(ticker, &wantTicker) {
|
||||||
t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker)
|
t.Fatalf("UpdateHistoricalTickers(eur) 1st pass = %v, want %v", *ticker, wantTicker)
|
||||||
}
|
}
|
||||||
|
|
||||||
// update historical tickers for the second time
|
// update historical tickers for the second time
|
||||||
err = fiatRates.downloader.UpdateHistoricalTickers()
|
err = fiatRates.downloader.UpdateHistoricalTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err)
|
t.Fatalf("UpdateHistoricalTickers 2nd pass failed with error: %v", err)
|
||||||
}
|
}
|
||||||
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
|
err = fiatRates.downloader.UpdateHistoricalTokenTickers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err)
|
t.Fatalf("UpdateHistoricalTokenTickers 2nd pass failed with error: %v", err)
|
||||||
}
|
}
|
||||||
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
ticker, err = fiatRates.db.FiatRatesFindLastTicker("usd", "")
|
||||||
if err != nil || ticker == nil {
|
if err != nil || ticker == nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
||||||
}
|
}
|
||||||
wantTicker = common.CurrencyRatesTicker{
|
wantTicker = common.CurrencyRatesTicker{
|
||||||
Rates: map[string]float32{
|
Rates: map[string]float32{
|
||||||
"aed": 240402.97,
|
"aed": 240402.97,
|
||||||
"ars": 240402.97,
|
"ars": 240402.97,
|
||||||
"aud": 240402.97,
|
"aud": 240402.97,
|
||||||
"btc": 240402.97,
|
"btc": 240402.97,
|
||||||
"eth": 240402.97,
|
"eth": 240402.97,
|
||||||
"eur": 240402.97,
|
"eur": 240402.97,
|
||||||
"ltc": 240402.97,
|
"ltc": 240402.97,
|
||||||
"usd": 1788.4183,
|
"usd": 1788.4183,
|
||||||
},
|
},
|
||||||
TokenRates: map[string]float32{
|
TokenRates: map[string]float32{
|
||||||
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
|
"0x5e9997684d061269564f94e5d11ba6ce6fa9528c": 4.1464476e+07,
|
||||||
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
|
"0x906710835d1ae85275eb770f06873340ca54274b": 4.1464476e+07,
|
||||||
},
|
},
|
||||||
Timestamp: time.Unix(1654819200, 0).UTC(),
|
Timestamp: time.Unix(1654819200, 0).UTC(),
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(ticker, &wantTicker) {
|
if !reflect.DeepEqual(ticker, &wantTicker) {
|
||||||
t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker)
|
t.Fatalf("UpdateHistoricalTickers(usd) 2nd pass = %v, want %v", *ticker, wantTicker)
|
||||||
}
|
}
|
||||||
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
|
ticker, err = fiatRates.db.FiatRatesFindLastTicker("eur", "")
|
||||||
if err != nil || ticker == nil {
|
if err != nil || ticker == nil {
|
||||||
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
t.Fatalf("FiatRatesFindLastTicker failed with error: %v", err)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(ticker, &wantTicker) {
|
if !reflect.DeepEqual(ticker, &wantTicker) {
|
||||||
t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker)
|
t.Fatalf("UpdateHistoricalTickers(eur) 2nd pass = %v, want %v", *ticker, wantTicker)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain"
|
"github.com/trezor/blockbook/bchain"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InternalServer is handle to internal http server
|
// InternalServer is handle to internal http server
|
||||||
@ -31,8 +32,8 @@ type InternalServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewInternalServer creates new internal http interface to blockbook and returns its handle
|
// NewInternalServer creates new internal http interface to blockbook and returns its handle
|
||||||
func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*InternalServer, error) {
|
func NewInternalServer(binding, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*InternalServer, error) {
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain"
|
"github.com/trezor/blockbook/bchain"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
)
|
)
|
||||||
|
|
||||||
const txsOnPage = 25
|
const txsOnPage = 25
|
||||||
@ -57,23 +58,24 @@ type PublicServer struct {
|
|||||||
explorerURL string
|
explorerURL string
|
||||||
internalExplorer bool
|
internalExplorer bool
|
||||||
is *common.InternalState
|
is *common.InternalState
|
||||||
|
fiatRates *fiat.FiatRates
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicServer creates new public server http interface to blockbook and returns its handle
|
// NewPublicServer creates new public server http interface to blockbook and returns its handle
|
||||||
// only basic functionality is mapped, to map all functions, call
|
// only basic functionality is mapped, to map all functions, call
|
||||||
func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, explorerURL string, metrics *common.Metrics, is *common.InternalState, debugMode bool) (*PublicServer, error) {
|
func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, explorerURL string, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates, debugMode bool) (*PublicServer, error) {
|
||||||
|
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
socketio, err := NewSocketIoServer(db, chain, mempool, txCache, metrics, is)
|
socketio, err := NewSocketIoServer(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
websocket, err := NewWebsocketServer(db, chain, mempool, txCache, metrics, is)
|
websocket, err := NewWebsocketServer(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -104,6 +106,7 @@ func NewPublicServer(binding string, certFiles string, db *db.RocksDB, chain bch
|
|||||||
explorerURL: explorerURL,
|
explorerURL: explorerURL,
|
||||||
internalExplorer: explorerURL == "",
|
internalExplorer: explorerURL == "",
|
||||||
is: is,
|
is: is,
|
||||||
|
fiatRates: fiatRates,
|
||||||
}
|
}
|
||||||
s.htmlTemplates.newTemplateData = s.newTemplateData
|
s.htmlTemplates.newTemplateData = s.newTemplateData
|
||||||
s.htmlTemplates.newTemplateDataWithError = s.newTemplateDataWithError
|
s.htmlTemplates.newTemplateDataWithError = s.newTemplateDataWithError
|
||||||
@ -377,10 +380,10 @@ func (s *PublicServer) newTemplateData(r *http.Request) *TemplateData {
|
|||||||
secondary = "usd"
|
secondary = "usd"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ticker := s.is.GetCurrentTicker(secondary, "")
|
ticker := s.fiatRates.GetCurrentTicker(secondary, "")
|
||||||
if ticker == nil && secondary != "usd" {
|
if ticker == nil && secondary != "usd" {
|
||||||
secondary = "usd"
|
secondary = "usd"
|
||||||
ticker = s.is.GetCurrentTicker(secondary, "")
|
ticker = s.fiatRates.GetCurrentTicker(secondary, "")
|
||||||
}
|
}
|
||||||
if ticker != nil {
|
if ticker != nil {
|
||||||
t.SecondaryCoin = strings.ToUpper(secondary)
|
t.SecondaryCoin = strings.ToUpper(secondary)
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain/coins/btc"
|
"github.com/trezor/blockbook/bchain/coins/btc"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
"github.com/trezor/blockbook/tests/dbtestdata"
|
"github.com/trezor/blockbook/tests/dbtestdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -121,7 +122,7 @@ func setupPublicHTTPServer(parser bchain.BlockChainParser, chain bchain.BlockCha
|
|||||||
}
|
}
|
||||||
|
|
||||||
// s.Run is never called, binding can be to any port
|
// s.Run is never called, binding can be to any port
|
||||||
s, err := NewPublicServer("localhost:12345", "", d, chain, mempool, txCache, "", metrics, is, false)
|
s, err := NewPublicServer("localhost:12345", "", d, chain, mempool, txCache, "", metrics, is, &fiat.FiatRates{}, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain"
|
"github.com/trezor/blockbook/bchain"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SocketIoServer is handle to SocketIoServer
|
// SocketIoServer is handle to SocketIoServer
|
||||||
@ -33,8 +34,8 @@ type SocketIoServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewSocketIoServer creates new SocketIo interface to blockbook and returns its handle
|
// NewSocketIoServer creates new SocketIo interface to blockbook and returns its handle
|
||||||
func NewSocketIoServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*SocketIoServer, error) {
|
func NewSocketIoServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*SocketIoServer, error) {
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/trezor/blockbook/bchain"
|
"github.com/trezor/blockbook/bchain"
|
||||||
"github.com/trezor/blockbook/common"
|
"github.com/trezor/blockbook/common"
|
||||||
"github.com/trezor/blockbook/db"
|
"github.com/trezor/blockbook/db"
|
||||||
|
"github.com/trezor/blockbook/fiat"
|
||||||
)
|
)
|
||||||
|
|
||||||
const upgradeFailed = "Upgrade failed: "
|
const upgradeFailed = "Upgrade failed: "
|
||||||
@ -70,8 +71,8 @@ type WebsocketServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewWebsocketServer creates new websocket interface to blockbook and returns its handle
|
// NewWebsocketServer creates new websocket interface to blockbook and returns its handle
|
||||||
func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState) (*WebsocketServer, error) {
|
func NewWebsocketServer(db *db.RocksDB, chain bchain.BlockChain, mempool bchain.Mempool, txCache *db.TxCache, metrics *common.Metrics, is *common.InternalState, fiatRates *fiat.FiatRates) (*WebsocketServer, error) {
|
||||||
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is)
|
api, err := api.NewWorker(db, chain, mempool, txCache, metrics, is, fiatRates)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user