Skip to content

Commit

Permalink
changes based on bucks review
Browse files Browse the repository at this point in the history
  • Loading branch information
ukane-philemon authored and ukane-philemon committed Jul 8, 2022
1 parent 03fabed commit d38fc2a
Show file tree
Hide file tree
Showing 33 changed files with 367 additions and 440 deletions.
264 changes: 137 additions & 127 deletions client/core/core.go

Large diffs are not rendered by default.

115 changes: 32 additions & 83 deletions client/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,8 @@ func newTestRig() *testRig {

locale: enUS,
localePrinter: message.NewPrinter(language.AmericanEnglish),

fiatRateSources: make(map[string]*commonRateSource),
},
db: tdb,
queue: queue,
Expand Down Expand Up @@ -9076,7 +9078,6 @@ func TestToggleRateSourceStatus(t *testing.T) {
rig := newTestRig()
defer rig.shutdown()
tCore := rig.core
tCore.exchangeRateSources = make(map[string]*commonSource)

tests := []struct {
name, source string
Expand All @@ -9095,136 +9096,84 @@ func TestToggleRateSourceStatus(t *testing.T) {
wantErr: false,
}}

// Test disabling exchange rate source.
// Test disabling fiat rate source.
for _, test := range tests {
err := tCore.ToggleRateSourceStatus(test.source, true)
if test.wantErr {
if err == nil {
t.Fatalf("%s: expected error", test.name)
}
continue
}
if err != nil {
t.Fatalf("%s: unexpected error: %v", test.name, err)
if test.wantErr != (err != nil) {
t.Fatalf("%s: wantErr = %t, err = %v", test.name, test.wantErr, err)
}
}

// Test enabling exchange rate source.
// Test enabling fiat rate source.
for _, test := range tests {
if test.init {
tCore.exchangeRateSources[test.source] = newcommonSource(tCore.log, tFetcher)
tCore.fiatRateSources[test.source] = newCommonRateSource(tCore.log, tFetcher)
}
err := tCore.ToggleRateSourceStatus(test.source, false)
if test.wantErr {
if err == nil {
t.Fatalf("%s: expected error", test.name)
}
continue
}
if err != nil {
t.Fatalf("%s: unexpected error: %v", test.name, err)
if test.wantErr != (err != nil) {
t.Fatalf("%s: wantErr = %t, err = %v", test.name, test.wantErr, err)
}
}
}

func TestIsConversionDisabled(t *testing.T) {
rig := newTestRig()
defer rig.shutdown()
tCore := rig.core
tCore.exchangeRateSources = make(map[string]*commonSource)

tests := []struct {
name string
init, disableConversion bool
}{{
name: "Conversion not disabled",
init: true,
}, {
name: "Conversion disabled",
disableConversion: true,
}}

for _, test := range tests {
if test.init {
tCore.exchangeRateSources["testsource"] = newcommonSource(tCore.log, tFetcher)
} else if len(tCore.exchangeRateSources) != 0 {
for token := range tCore.exchangeRateSources {
delete(tCore.exchangeRateSources, token)
}
}
isDisabled := tCore.isConversionDisabled()
if test.disableConversion {
if isDisabled {
continue
}
t.Fatalf("%s: expected fiat conversion to be disabled", test.name)
}
if isDisabled {
t.Fatalf("%s: fiat conversion should not be diabled", test.name)
}

}

}

func TestExchangeRateSources(t *testing.T) {
func TestFiatRateSources(t *testing.T) {
rig := newTestRig()
defer rig.shutdown()
tCore := rig.core
tCore.exchangeRateSources = make(map[string]*commonSource)
supportedFetchers := len(exchangeRateFetchers)
rateSources := tCore.ExchangeRateSources()
supportedFetchers := len(fiatRateFetchers)
rateSources := tCore.FiatRateSources()
if len(rateSources) != supportedFetchers {
t.Fatalf("Expected %d number of exchange rate source/fetchers", supportedFetchers)
t.Fatalf("Expected %d number of fiat rate source/fetchers", supportedFetchers)
}
}

func TestFetchAllFiatRates(t *testing.T) {
func TestFiatConversions(t *testing.T) {
rig := newTestRig()
defer rig.shutdown()
tCore := rig.core
tCore.exchangeRateSources = make(map[string]*commonSource)
ctx, cancel := context.WithCancel(tCore.ctx)
tCore.stopFiatRateFetching = cancel

// No exchange rate source initialized
fiatRates := tCore.fetchAllFiatRates()
// No fiat rate source initialized
fiatRates := tCore.fiatConversions()
if len(fiatRates) != 0 {
t.Fatal("Unexpected asset rate values.")
}

// Initialize exchange rate sources.
for token := range exchangeRateFetchers {
tCore.exchangeRateSources[token] = newcommonSource(tCore.log, tFetcher)
// Initialize fiat rate sources.
for token := range fiatRateFetchers {
tCore.fiatRateSources[token] = newCommonRateSource(tCore.log, tFetcher)
}

// Fetch asset rates.
// Fetch fiat rates.
tCore.wg.Add(1)
go func() {
defer tCore.wg.Done()
tCore.refreshExchangeRate()
tCore.refreshFiatRates(ctx)
}()
tCore.wg.Wait()

// Expects assets exchange rate values.
fiatRates = tCore.fetchAllFiatRates()
// Expects assets fiat rate values.
fiatRates = tCore.fiatConversions()
if len(fiatRates) != 2 {
t.Fatal("Expected assets exchange rate for two assets")
t.Fatal("Expected assets fiat rate for two assets")
}

// Exchange rates for assets can expire, and exchange rate fetchers can be
// fiat rates for assets can expire, and fiat rate fetchers can be
// removed if expired.
time.Sleep(2 * time.Second)
for token, source := range tCore.exchangeRateSources {
for token, source := range tCore.fiatRateSources {
if source.isExpired(2 * time.Second) {
delete(tCore.exchangeRateSources, token)
delete(tCore.fiatRateSources, token)
}
}

fiatRates = tCore.fetchAllFiatRates()
fiatRates = tCore.fiatConversions()
if len(fiatRates) != 0 {
t.Fatal("Unexpected assets exchange rate values, expected to ignore expired exchange rates.")
t.Fatal("Unexpected assets fiat rate values, expected to ignore expired fiat rates.")
}

if !tCore.isConversionDisabled() {
if len(tCore.fiatRateSources) != 0 {
t.Fatal("Expected fiat conversion to be disabled, all rate source data has expired.")
}
}
69 changes: 30 additions & 39 deletions client/core/exchangeratefetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"encoding/json"
"fmt"

"net/http"
"strings"
"sync"
Expand All @@ -24,7 +23,7 @@ const (
// fiatRateDataExpiry : Any data older than fiatRateDataExpiry will be discarded.
fiatRateDataExpiry = 60 * time.Minute

// Tokens. Used to identify exchange rate source.
// Tokens. Used to identify fiat rate source.
messari = "messari"
coinpaprika = "coinpaprika"
dcrdataDotOrg = "dcrdata"
Expand All @@ -38,24 +37,24 @@ var (
dcrBipId, _ = dex.BipSymbolID("dcr")
)

// exchangeRateFetchers is the list of all supported exchange rate fetchers.
var exchangeRateFetchers = map[string]rateFetcher{
// fiatRateFetchers is the list of all supported fiat rate fetchers.
var fiatRateFetchers = map[string]rateFetcher{
coinpaprika: fetchCoinpaprikaRates,
dcrdataDotOrg: fetchDcrdataRates,
messari: fetchMassariRates,
messari: fetchMessariRates,
}

// fiatRateInfo holds the fiat exchange rate and the last update time for an
// fiatRateInfo holds the fiat rate and the last update time for an
// asset.
type fiatRateInfo struct {
rate float64
lastUpdate time.Time
}

// rateFetcher can fetch exchange rates for assets from an API.
// rateFetcher can fetch fiat rates for assets from an API.
type rateFetcher func(context context.Context, logger dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64

type commonSource struct {
type commonRateSource struct {
mtx sync.RWMutex
lastRequest time.Time
log dex.Logger
Expand All @@ -64,22 +63,22 @@ type commonSource struct {
}

// logRequest sets the lastRequest time.Time.
func (source *commonSource) logRequest() {
func (source *commonRateSource) logRequest() {
source.mtx.Lock()
defer source.mtx.Unlock()
source.lastRequest = time.Now()
}

// lastTry is the last time the exchange rate fecther made a request.
func (source *commonSource) lastTry() time.Time {
// lastTry is the last time the fiat rate fetcher made a request.
func (source *commonRateSource) lastTry() time.Time {
source.mtx.RLock()
defer source.mtx.RUnlock()
return source.lastRequest
}

// isExpired checks the last update time for all exchange rates against the
// isExpired checks the last update time for all fiat rates against the
// provided expiryTime.
func (source *commonSource) isExpired(expiryTime time.Duration) bool {
func (source *commonRateSource) isExpired(expiryTime time.Duration) bool {
source.mtx.Lock()
defer source.mtx.Unlock()
var expiredCount int
Expand All @@ -92,20 +91,20 @@ func (source *commonSource) isExpired(expiryTime time.Duration) bool {
return expiredCount == totalFiatRate && totalFiatRate != 0
}

// assetRate returns the exchange rate information for the assetID specified.
// found is true is the rate source has a value for the assetID specified.
func (source *commonSource) assetRate(assetID uint32) (fiatRateInfo, bool) {
// assetRate returns the fiat rate information for the assetID specified.
func (source *commonRateSource) assetRate(assetID uint32) *fiatRateInfo {
source.mtx.Lock()
defer source.mtx.Unlock()
rateInfo, found := source.fiatRates[assetID]
if !found {
return fiatRateInfo{}, false
rateInfo := source.fiatRates[assetID]
var fiatRateInfo fiatRateInfo
if rateInfo != nil {
fiatRateInfo = *rateInfo
}
return *rateInfo, true
return &fiatRateInfo
}

// refreshRates updates the last update time and the rate information for asset.
func (source *commonSource) refreshRates(ctx context.Context, assets map[uint32]*SupportedAsset) {
// refreshRates updates the last update time and the rate information for assets.
func (source *commonRateSource) refreshRates(ctx context.Context, assets map[uint32]*SupportedAsset) {
source.logRequest()
fiatRates := source.fetchRates(ctx, source.log, assets)
source.mtx.Lock()
Expand All @@ -118,17 +117,16 @@ func (source *commonSource) refreshRates(ctx context.Context, assets map[uint32]
source.mtx.Unlock()
}

// Used to initialize an exchange rate source.
func newcommonSource(logger dex.Logger, fetcher rateFetcher) *commonSource {
return &commonSource{
// Used to initialize a fiat rate source.
func newCommonRateSource(logger dex.Logger, fetcher rateFetcher) *commonRateSource {
return &commonRateSource{
log: logger,
fetchRates: fetcher,
fiatRates: make(map[uint32]*fiatRateInfo),
}
}

// fetchCoinpaprikaRates retrieves and parses exchange rate data from the
// coinpaprika API. coinpaprika response models the JSON data returned from the
// fetchCoinpaprikaRates retrieves and parses fiat rate data from the
// coinpaprika API. See https://api.coinpaprika.com/#operation/getTickersById
// for sample request and response information.
func fetchCoinpaprikaRates(ctx context.Context, log dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64 {
Expand All @@ -140,8 +138,6 @@ func fetchCoinpaprikaRates(ctx context.Context, log dex.Logger, assets map[uint3
}

res := new(struct {
Name string `json:"name"`
Symbol string `json:"symbol"`
Quotes struct {
Currency struct {
Price float64 `json:"price"`
Expand Down Expand Up @@ -179,7 +175,7 @@ func fetchCoinpaprikaRates(ctx context.Context, log dex.Logger, assets map[uint3
return fiatRates
}

// fetchDcrdataRates retrieves and parses exchange rate data from dcrdataDotOrg
// fetchDcrdataRates retrieves and parses fiat rate data from dcrdata
// exchange rate API.
func fetchDcrdataRates(ctx context.Context, log dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64 {
assetBTC := assets[btcBipId]
Expand All @@ -192,7 +188,6 @@ func fetchDcrdataRates(ctx context.Context, log dex.Logger, assets map[uint32]*S

fiatRates := make(map[uint32]float64)
res := new(struct {
Currency string `json:"btcIndex"`
DcrPrice float64 `json:"dcrPrice"`
BtcPrice float64 `json:"btcPrice"`
})
Expand Down Expand Up @@ -227,12 +222,10 @@ func fetchDcrdataRates(ctx context.Context, log dex.Logger, assets map[uint32]*S
return fiatRates
}

// fetchMassariRates retrieves and parses exchange rate data from the massari
// API. It returns the last error if any. messari response models the JSON data
// returned from the messari API. See
// https://messari.io/api/docs#operation/Get%20Asset%20Metrics for sample
// request and response information.
func fetchMassariRates(ctx context.Context, log dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64 {
// fetchMessariRates retrieves and parses fiat rate data from the Messari API.
// See https://messari.io/api/docs#operation/Get%20Asset%20Market%20Data for
// sample request and response information.
func fetchMessariRates(ctx context.Context, log dex.Logger, assets map[uint32]*SupportedAsset) map[uint32]float64 {
fiatRates := make(map[uint32]float64)
for assetID, asset := range assets {
if asset.Wallet == nil {
Expand All @@ -242,8 +235,6 @@ func fetchMassariRates(ctx context.Context, log dex.Logger, assets map[uint32]*S

res := new(struct {
Data struct {
Name string `json:"name"`
Symbol string `json:"symbol"`
MarketData struct {
Price float64 `json:"price_usd"`
} `json:"market_data"`
Expand Down
1 change: 0 additions & 1 deletion client/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ type User struct {
SeedGenerationTime uint64 `json:"seedgentime"`
Assets map[uint32]*SupportedAsset `json:"assets"`
FiatRates map[uint32]float64 `json:"fiatRates"`
DisableConversion bool `json:"disableConversion"`
}

// SupportedAsset is data about an asset and possibly the wallet associated
Expand Down
4 changes: 2 additions & 2 deletions client/db/bolt/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,7 @@ func (db *BoltDB) DeleteInactiveMatches(ctx context.Context, olderThan *time.Tim
return nil
}

// SaveDisabledRateSources updates disabled exchange rate sources.
// SaveDisabledRateSources updates disabled fiat rate sources.
func (db *BoltDB) SaveDisabledRateSources(disabledSources []string) error {
return db.Update(func(tx *bbolt.Tx) error {
bkt := tx.Bucket(appBucket)
Expand All @@ -2087,7 +2087,7 @@ func (db *BoltDB) SaveDisabledRateSources(disabledSources []string) error {
})
}

// DisabledRateSources retrieves a map of disabled exchange rate sources.
// DisabledRateSources retrieves a map of disabled fiat rate sources.
func (db *BoltDB) DisabledRateSources() (disabledSources []string, err error) {
return disabledSources, db.View(func(tx *bbolt.Tx) error {
bkt := tx.Bucket(appBucket)
Expand Down
Loading

0 comments on commit d38fc2a

Please sign in to comment.