diff --git a/.gitignore b/.gitignore index 190a006..4dd2c12 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,6 @@ build/ cmd/feederd/tdexd -cmd/feederd/config.json +cmd/feederd/config*.json -config.json \ No newline at end of file +config.json diff --git a/cmd/feederd/config.test.json b/cmd/feederd/config.test.json index 0278506..d9e857d 100755 --- a/cmd/feederd/config.test.json +++ b/cmd/feederd/config.test.json @@ -1,10 +1 @@ -{ - "daemon_endpoint":"127.0.0.1:9000", - "kraken_ws_endpoint":"ws.kraken.com", - "markets": [ - { - "base_asset":"5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225","quote_asset":"bffce3908a595436b6ab08f916fea2c9fc6a702f46b268ca354205d127f60c48","kraken_ticker":"LTC/USDT", - "interval":500 - } - ] -} \ No newline at end of file +{"daemon_endpoint":"127.0.0.1:9000","kraken_ws_endpoint":"ws.kraken.com","markets":[{"base_asset":"5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225","quote_asset":"91b988204bfb8568d81d7c2962c5f37d6e413e5efaa36c417d245ba2237e8320","kraken_ticker":"LTC/USDT","interval":500}]} \ No newline at end of file diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index a270b96..e906021 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -21,7 +21,7 @@ const ( daemonEndpoint = "127.0.0.1:9000" krakenWsEndpoint = "ws.kraken.com" // nigiriUrl = "https://nigiri.network/liquid/api" - nigiriUrl = "http://localhost:3001" + nigiriURL = "http://localhost:3001" password = "vulpemsecret" ) @@ -41,11 +41,11 @@ func TestFeeder(t *testing.T) { func runDaemonAndInitConfigFile(t *testing.T) { usdt := runDaemonAndCreateMarket(t) - configJson := adapters.ConfigJson{ + configJSON := adapters.ConfigJSON{ DaemonEndpoint: daemonEndpoint, KrakenWsEndpoint: krakenWsEndpoint, - Markets: []adapters.MarketJson{ - adapters.MarketJson{ + Markets: []adapters.MarketJSON{ + adapters.MarketJSON{ KrakenTicker: "LTC/USDT", BaseAsset: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", QuoteAsset: usdt, @@ -54,7 +54,7 @@ func runDaemonAndInitConfigFile(t *testing.T) { }, } - bytes, err := json.Marshal(configJson) + bytes, err := json.Marshal(configJSON) if err != nil { t.Error(err) } @@ -72,7 +72,7 @@ func runDaemonAndCreateMarket(t *testing.T) string { "-d", "-v", "tdexd:/.tdex-daemon", "-e", "TDEX_NETWORK=regtest", - "-e", "TDEX_EXPLORER_ENDPOINT="+nigiriUrl, + "-e", "TDEX_EXPLORER_ENDPOINT="+nigiriURL, "-e", "TDEX_FEE_ACCOUNT_BALANCE_TRESHOLD=1000", "-e", "TDEX_BASE_ASSET=5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", "-e", "TDEX_LOG_LEVEL=5", @@ -107,14 +107,14 @@ func runDaemonAndCreateMarket(t *testing.T) string { t.Error(err) } - depositMarketJson, err := runCLICommand("depositmarket", "--base_asset", "", "--quote_asset", "") + depositMarketJSON, err := runCLICommand("depositmarket", "--base_asset", "", "--quote_asset", "") if err != nil { t.Error(err) } var depositMarketResult map[string]interface{} - err = json.Unmarshal([]byte(depositMarketJson), &depositMarketResult) + err = json.Unmarshal([]byte(depositMarketJSON), &depositMarketResult) if err != nil { t.Error(t, err) } @@ -153,7 +153,7 @@ func fundMarketAddress(t *testing.T, address string) string { } func mint(address string, amount int) (string, string, error) { - url := fmt.Sprintf("%s/mint", nigiriUrl) + url := fmt.Sprintf("%s/mint", nigiriURL) payload := map[string]interface{}{"address": address, "quantity": amount} body, _ := json.Marshal(payload) resp, err := http.Post(url, "application/json", bytes.NewBuffer(body)) @@ -183,7 +183,7 @@ func mint(address string, amount int) (string, string, error) { } func faucet(address string) (string, error) { - url := fmt.Sprintf("%s/faucet", nigiriUrl) + url := fmt.Sprintf("%s/faucet", nigiriURL) payload := map[string]string{"address": address} body, _ := json.Marshal(payload) resp, err := http.Post(url, "application/json", bytes.NewBuffer(body)) diff --git a/config/config.go b/config/config.go index e9bdebe..5f6055d 100644 --- a/config/config.go +++ b/config/config.go @@ -30,6 +30,7 @@ func init() { vip.SetDefault(ConfigFilePathKey, "./config.json") } +// GetConfigPath return the path of the config.json file func GetConfigPath() string { return vip.GetString(ConfigFilePathKey) } diff --git a/go.mod b/go.mod index f46f8c1..d7d8e5f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.15 require ( github.com/aopoltorzhicky/go_kraken/websocket v0.0.10 - github.com/prometheus/common v0.4.0 github.com/sirupsen/logrus v1.7.0 github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index c78201a..d02c4e8 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -9,19 +9,22 @@ import ( "github.com/tdex-network/tdex-feeder/internal/domain" ) -type MarketJson struct { +// MarketJSON is the struct describing the shape of market specs in config JSON file +type MarketJSON struct { BaseAsset string `json:"base_asset"` QuoteAsset string `json:"quote_asset"` KrakenTicker string `json:"kraken_ticker"` Interval int `json:"interval"` } -type ConfigJson struct { +// ConfigJSON is the struct describing the shape of config JSON file +type ConfigJSON struct { DaemonEndpoint string `json:"daemon_endpoint"` KrakenWsEndpoint string `json:"kraken_ws_endpoint"` - Markets []MarketJson `json:"markets"` + Markets []MarketJSON `json:"markets"` } +// Config is the config of the application retreived from config JSON file type Config struct { daemonEndpoint string krakenWSaddress string @@ -29,6 +32,7 @@ type Config struct { marketIntervals map[domain.Market]time.Duration } +// ToFeederService transforms a Config into FeederService func (config *Config) ToFeederService() application.FeederService { feederSvc := application.NewFeederService(application.NewFeederServiceArgs{ KrakenWSaddress: config.krakenWSaddress, @@ -40,8 +44,9 @@ func (config *Config) ToFeederService() application.FeederService { return feederSvc } +// UnmarshalJSON ... func (config *Config) UnmarshalJSON(data []byte) error { - jsonConfig := &ConfigJson{} + jsonConfig := &ConfigJSON{} err := json.Unmarshal(data, jsonConfig) if err != nil { return err @@ -58,14 +63,14 @@ func (config *Config) UnmarshalJSON(data []byte) error { configTickerToMarketMap := make(map[string]domain.Market) marketIntervalsMap := make(map[domain.Market]time.Duration) - for _, marketJson := range jsonConfig.Markets { + for _, marketJSON := range jsonConfig.Markets { market := domain.Market{ - BaseAsset: marketJson.BaseAsset, - QuoteAsset: marketJson.QuoteAsset, + BaseAsset: marketJSON.BaseAsset, + QuoteAsset: marketJSON.QuoteAsset, } - configTickerToMarketMap[marketJson.KrakenTicker] = market - marketIntervalsMap[market] = time.Duration(marketJson.Interval) * time.Millisecond + configTickerToMarketMap[marketJSON.KrakenTicker] = market + marketIntervalsMap[market] = time.Duration(marketJSON.Interval) * time.Millisecond } config.markets = configTickerToMarketMap @@ -74,7 +79,7 @@ func (config *Config) UnmarshalJSON(data []byte) error { return nil } -func (configJson ConfigJson) validate() error { +func (configJson ConfigJSON) validate() error { if configJson.DaemonEndpoint == "" { return ErrDaemonEndpointIsEmpty } @@ -87,22 +92,22 @@ func (configJson ConfigJson) validate() error { return ErrNeedAtLeastOneMarketToFeed } - for _, marketJson := range configJson.Markets { - if marketJson.KrakenTicker == "" { + for _, marketJSON := range configJson.Markets { + if marketJSON.KrakenTicker == "" { return ErrKrakenTickerIsEmpty } - err := validateAssetString(marketJson.BaseAsset) + err := validateAssetString(marketJSON.BaseAsset) if err != nil { return err } - err = validateAssetString(marketJson.QuoteAsset) + err = validateAssetString(marketJSON.QuoteAsset) if err != nil { return err } - if marketJson.Interval < 0 { + if marketJSON.Interval < 0 { return ErrIntervalIsNotPositiveNumber } } diff --git a/internal/adapters/errors.go b/internal/adapters/errors.go index ef9bfa6..266486c 100644 --- a/internal/adapters/errors.go +++ b/internal/adapters/errors.go @@ -3,17 +3,25 @@ package adapters import "errors" var ( - ErrDaemonEndpointIsEmpty = errors.New("daemon endpoint is empty") - ErrKrakenEndpointIsEmpty = errors.New("kraken websocket endpoint is empty") - ErrNeedAtLeastOneMarketToFeed = errors.New("need at least 1 market to feed") - ErrKrakenTickerIsEmpty = errors.New("krakenTicker should not be an empty string") + // ErrDaemonEndpointIsEmpty is returned if the config contains an empty tdex-daemon endpoint + ErrDaemonEndpointIsEmpty = errors.New("daemon endpoint is empty") + // ErrKrakenEndpointIsEmpty is returned if the config contains an empty kraken WS endpoint + ErrKrakenEndpointIsEmpty = errors.New("kraken websocket endpoint is empty") + // ErrNeedAtLeastOneMarketToFeed is returned if the config does not contain market to feed + ErrNeedAtLeastOneMarketToFeed = errors.New("need at least 1 market to feed") + // ErrKrakenTickerIsEmpty is returned if the ticker specified in config is an empty string + ErrKrakenTickerIsEmpty = errors.New("krakenTicker should not be an empty string") + // ErrIntervalIsNotPositiveNumber is returned if the interval is < 0 ErrIntervalIsNotPositiveNumber = errors.New("interval must be greater (or equal) than 0") ) +// ErrInvalidAssetHash is returned if the given string `asset` is not a valid asset hash string type ErrInvalidAssetHash struct { asset string } +var _ error = &ErrInvalidAssetHash{} + func (e ErrInvalidAssetHash) Error() string { return "the string '" + e.asset + "' is an invalid asset string." } diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index ac739b2..007dbc7 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -7,6 +7,7 @@ import ( "github.com/tdex-network/tdex-feeder/internal/ports" ) +// FeedService is the interface wrapping krakenWS and transform it into a domain.Feed type FeedService interface { Start() Stop() @@ -20,6 +21,7 @@ type krakenFeedService struct { tickersToMarketMap map[string]domain.Market } +// NewKrakenFeedService is the factory function for FeedService func NewKrakenFeedService( address string, tickersToMarketMap map[string]domain.Market, diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go index 5f1227e..113c4b6 100644 --- a/internal/application/feed_service_test.go +++ b/internal/application/feed_service_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/tdex-network/tdex-feeder/internal/domain" + "github.com/tdex-network/tdex-feeder/pkg/feeder" + "github.com/tdex-network/tdex-feeder/pkg/testutils" ) const ( @@ -30,8 +32,8 @@ func TestKrakenFeedService(t *testing.T) { defer svc.Stop() feed := svc.GetFeed() - target := &mockTarget{marketPrices: []domain.MarketPrice{}} - feeder := NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) + target := &testutils.MockTarget{MarketPrices: []domain.MarketPrice{}} + feeder := feeder.NewFeeder([]domain.Feed{feed}, []domain.Target{target}) go func() { err := feeder.Start() if err != nil { @@ -42,5 +44,5 @@ func TestKrakenFeedService(t *testing.T) { time.Sleep(10 * time.Second) feeder.Stop() - assert.Equal(t, true, len(target.marketPrices) > 0) + assert.Equal(t, true, len(target.MarketPrices) > 0) } diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go index 2d149c9..32e4ab7 100644 --- a/internal/application/feeder_service.go +++ b/internal/application/feeder_service.go @@ -5,19 +5,22 @@ import ( log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" + "github.com/tdex-network/tdex-feeder/pkg/feeder" ) +// FeederService is a tdex-configured feeder. It takes data from KrakenWS endpoint and update a tdex daemon type FeederService interface { Start() error Stop() error } type feederService struct { - tdexFeeder TdexFeeder + tdexFeeder feeder.Service krakenService FeedService target *TdexDaemonTarget } +// NewFeederServiceArgs is a wrapper for NewFeederService arguments type NewFeederServiceArgs struct { OperatorEndpoint string MarketToInterval map[domain.Market]time.Duration @@ -25,6 +28,7 @@ type NewFeederServiceArgs struct { TickerToMarket map[string]domain.Market } +// NewFeederService is the factory function for the FeederService func NewFeederService(args NewFeederServiceArgs) FeederService { target := NewTdexDaemonTarget(args.OperatorEndpoint, args.MarketToInterval) @@ -33,7 +37,7 @@ func NewFeederService(args NewFeederServiceArgs) FeederService { log.Fatal(err) } - feeder := NewTdexFeeder( + feeder := feeder.NewFeeder( []domain.Feed{krakenFeedService.GetFeed()}, []domain.Target{target}, ) diff --git a/internal/application/test_utils.go b/internal/application/test_utils.go deleted file mode 100644 index 3245c13..0000000 --- a/internal/application/test_utils.go +++ /dev/null @@ -1,11 +0,0 @@ -package application - -import "github.com/tdex-network/tdex-feeder/internal/domain" - -type mockTarget struct { - marketPrices []domain.MarketPrice -} - -func (t *mockTarget) Push(marketPrice domain.MarketPrice) { - t.marketPrices = append(t.marketPrices, marketPrice) -} diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go index 5aa6a78..3fc64d9 100644 --- a/internal/application/updater_service.go +++ b/internal/application/updater_service.go @@ -10,7 +10,7 @@ import ( "github.com/tdex-network/tdex-feeder/internal/ports" ) -// Implements the domain.Target interface and manage interval for each market +// TdexDaemonTarget implements the domain.Target interface and manage interval for each market type TdexDaemonTarget struct { Endpoint string priceUpdater ports.TdexDaemonPriceUpdater diff --git a/internal/domain/feed.go b/internal/domain/feed.go index 801c68d..de8cdf4 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -1,5 +1,6 @@ package domain +// MarketPrice represents a new price associated with a given market type MarketPrice struct { Market Market Price Price diff --git a/internal/domain/market.go b/internal/domain/market.go index c954a50..2de7ff1 100644 --- a/internal/domain/market.go +++ b/internal/domain/market.go @@ -1,5 +1,6 @@ package domain +// Market is represented by his base asset & his quote asset type Market struct { BaseAsset string QuoteAsset string diff --git a/internal/domain/price.go b/internal/domain/price.go index c017327..78d7166 100644 --- a/internal/domain/price.go +++ b/internal/domain/price.go @@ -1,5 +1,6 @@ package domain +// Price represents a price for a market type Price struct { BasePrice float32 QuotePrice float32 diff --git a/internal/domain/target.go b/internal/domain/target.go index a93beeb..0b022b4 100644 --- a/internal/domain/target.go +++ b/internal/domain/target.go @@ -1,5 +1,6 @@ package domain +// Target interface is used to push new prices fetched from feeds type Target interface { Push(marketPrice MarketPrice) } diff --git a/internal/ports/kraken_websocket.go b/internal/ports/kraken_websocket.go index 1e377a1..5347b77 100644 --- a/internal/ports/kraken_websocket.go +++ b/internal/ports/kraken_websocket.go @@ -7,6 +7,7 @@ import ( log "github.com/sirupsen/logrus" ) +// KrakenWebSocket is the interface to manage kraken web socket streams type KrakenWebSocket interface { Connect(address string, tickersToSubscribe []string) error StartListen() (chan TickerWithPrice, error) @@ -18,6 +19,7 @@ type krakenWebSocket struct { tickerWithPriceChan chan TickerWithPrice } +// NewKrakenWebSocket is a factory function for KrakenWebSocket interface func NewKrakenWebSocket() KrakenWebSocket { return &krakenWebSocket{ krakenWS: ws.New(), diff --git a/internal/ports/tdex_daemon_price_updater.go b/internal/ports/tdex_daemon_price_updater.go index 87bbb45..75823a4 100644 --- a/internal/ports/tdex_daemon_price_updater.go +++ b/internal/ports/tdex_daemon_price_updater.go @@ -12,6 +12,7 @@ import ( "google.golang.org/grpc" ) +// TdexDaemonPriceUpdater is a grpc client using to call UpdateMarketPrice RPC of tdex daemon type TdexDaemonPriceUpdater interface { UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error } diff --git a/internal/ports/types.go b/internal/ports/types.go index fc4119d..8775e49 100644 --- a/internal/ports/types.go +++ b/internal/ports/types.go @@ -1,5 +1,6 @@ package ports +// TickerWithPrice is a struct using to represent ticker to subscribe in kraken web socket feed type TickerWithPrice struct { Ticker string Price float64 diff --git a/internal/application/tdex_feeder.go b/pkg/feeder/feeder.go similarity index 80% rename from internal/application/tdex_feeder.go rename to pkg/feeder/feeder.go index 6594ad5..d799eb7 100644 --- a/internal/application/tdex_feeder.go +++ b/pkg/feeder/feeder.go @@ -1,4 +1,4 @@ -package application +package feeder import ( "errors" @@ -8,13 +8,14 @@ import ( "github.com/tdex-network/tdex-feeder/internal/domain" ) -type TdexFeeder interface { +// Service is the interface describing the feeder behavior +type Service interface { Start() error Stop() IsRunning() bool } -type tdexFeeder struct { +type feederService struct { feeds []domain.Feed targets []domain.Target stopChan chan bool @@ -22,8 +23,9 @@ type tdexFeeder struct { locker sync.Locker } -func NewTdexFeeder(feeds []domain.Feed, targets []domain.Target) TdexFeeder { - return &tdexFeeder{ +// NewFeeder is the factory function for feeder service +func NewFeeder(feeds []domain.Feed, targets []domain.Target) Service { + return &feederService{ feeds: feeds, targets: targets, stopChan: make(chan bool), @@ -34,7 +36,7 @@ func NewTdexFeeder(feeds []domain.Feed, targets []domain.Target) TdexFeeder { // Start observe all the feeds chan (using merge function) // and push the results to all targets -func (t *tdexFeeder) Start() error { +func (t *feederService) Start() error { if t.IsRunning() { return errors.New("the feeder is already started") } @@ -59,11 +61,11 @@ func (t *tdexFeeder) Start() error { return nil } -func (t *tdexFeeder) Stop() { +func (t *feederService) Stop() { t.stopChan <- true } -func (t *tdexFeeder) IsRunning() bool { +func (t *feederService) IsRunning() bool { t.locker.Lock() defer t.locker.Unlock() return t.running diff --git a/internal/application/tdex_feeder_test.go b/pkg/feeder/feeder_test.go similarity index 79% rename from internal/application/tdex_feeder_test.go rename to pkg/feeder/feeder_test.go index 49fb4b2..045a4e6 100644 --- a/internal/application/tdex_feeder_test.go +++ b/pkg/feeder/feeder_test.go @@ -1,4 +1,4 @@ -package application +package feeder import ( "testing" @@ -6,17 +6,18 @@ import ( "github.com/stretchr/testify/assert" "github.com/tdex-network/tdex-feeder/internal/domain" + "github.com/tdex-network/tdex-feeder/pkg/testutils" ) func TestFeeder(t *testing.T) { feed := domain.NewFeed() feedBis := domain.NewFeed() - target := &mockTarget{ - marketPrices: make([]domain.MarketPrice, 0), + target := &testutils.MockTarget{ + MarketPrices: make([]domain.MarketPrice, 0), } - feeder := NewTdexFeeder( + feeder := NewFeeder( []domain.Feed{feed, feedBis}, []domain.Target{target}, ) @@ -55,5 +56,5 @@ func TestFeeder(t *testing.T) { time.Sleep(500 * time.Millisecond) feeder.Stop() - assert.Equal(t, 15, len(target.marketPrices)) + assert.Equal(t, 15, len(target.MarketPrices)) } diff --git a/pkg/testutils/mock_target.go b/pkg/testutils/mock_target.go new file mode 100644 index 0000000..00145f0 --- /dev/null +++ b/pkg/testutils/mock_target.go @@ -0,0 +1,13 @@ +package testutils + +import "github.com/tdex-network/tdex-feeder/internal/domain" + +// MockTarget simulates a target interface +type MockTarget struct { + MarketPrices []domain.MarketPrice +} + +// Push is the target method +func (t *MockTarget) Push(marketPrice domain.MarketPrice) { + t.MarketPrices = append(t.MarketPrices, marketPrice) +}