From f07ec2bfabe853d4f765a8715388371831289cf2 Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 27 Nov 2020 19:42:40 +0100 Subject: [PATCH 01/70] initial domain implementation --- internal/domain/feed.go | 36 +++++++++++++++++++++ internal/domain/market.go | 6 ++++ internal/domain/price.go | 6 ++++ internal/domain/target.go | 5 +++ internal/domain/tdexFeeder.go | 61 +++++++++++++++++++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 internal/domain/feed.go create mode 100644 internal/domain/market.go create mode 100644 internal/domain/price.go create mode 100644 internal/domain/target.go create mode 100644 internal/domain/tdexFeeder.go diff --git a/internal/domain/feed.go b/internal/domain/feed.go new file mode 100644 index 0000000..2559bca --- /dev/null +++ b/internal/domain/feed.go @@ -0,0 +1,36 @@ +package domain + +import "sync" + +type MarketPrice struct { + Market Market + Price Price +} + +type Feed interface { + getId() string + getMarketPriceFeed() <-chan MarketPrice +} + +func merge(feeds ...Feed) <-chan MarketPrice { + mergedChan := make(chan MarketPrice) + var wg sync.WaitGroup + + wg.Add(len(feeds)) + for _, feed := range feeds { + c := feed.getMarketPriceFeed() + go func(c <-chan MarketPrice) { + for marketPrice := range c { + mergedChan <- marketPrice + } + wg.Done() + }(c) + } + + go func() { + wg.Wait() + close(mergedChan) + }() + + return mergedChan +} \ No newline at end of file diff --git a/internal/domain/market.go b/internal/domain/market.go new file mode 100644 index 0000000..3eb8a1b --- /dev/null +++ b/internal/domain/market.go @@ -0,0 +1,6 @@ +package domain + +type Market struct { + BaseAsset string + QuoteAsset string +} \ No newline at end of file diff --git a/internal/domain/price.go b/internal/domain/price.go new file mode 100644 index 0000000..534fb44 --- /dev/null +++ b/internal/domain/price.go @@ -0,0 +1,6 @@ +package domain + +type Price struct { + BasePrice float32 + QuotePrice float32 +} \ No newline at end of file diff --git a/internal/domain/target.go b/internal/domain/target.go new file mode 100644 index 0000000..5274ae4 --- /dev/null +++ b/internal/domain/target.go @@ -0,0 +1,5 @@ +package domain + +type Target interface { + Push(marketPrice MarketPrice) error +} \ No newline at end of file diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go new file mode 100644 index 0000000..5a04191 --- /dev/null +++ b/internal/domain/tdexFeeder.go @@ -0,0 +1,61 @@ +package domain + +import "errors" + + +type TdexFeeder interface { + Start() error + Stop() + IsRunning() bool +} + +type tdexFeeder struct { + feeds []Feed + targets []Target + stopChan chan bool + running bool +} + +func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { + return &tdexFeeder{ + feeds: feeds, + targets: targets, + } +} + +func (t *tdexFeeder) Start() error { + if t.IsRunning() { + return errors.New("the feeder is already started") + } + + t.running = true + marketPriceChannel := merge(t.feeds...) + + for t.running { + select { + case <-t.stopChan: + t.running = false + break; + case marketPrice := <-marketPriceChannel: + for _, target := range t.targets { + err := target.Push(marketPrice) + if err != nil { + panic(err) + } + } + } + } + + return nil +} + +func (t *tdexFeeder) Stop() { + t.stopChan <- true +} + +func (t *tdexFeeder) IsRunning() bool { + return t.running +} + + + From d4df49d4290ad4b055a21cbaf97821d83b8cd404 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 10:52:58 +0100 Subject: [PATCH 02/70] feed domain --- internal/domain/feed.go | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/internal/domain/feed.go b/internal/domain/feed.go index 2559bca..c662586 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -1,6 +1,10 @@ package domain -import "sync" +import ( + "sync" + + "github.com/google/uuid" +) type MarketPrice struct { Market Market @@ -8,8 +12,33 @@ type MarketPrice struct { } type Feed interface { - getId() string - getMarketPriceFeed() <-chan MarketPrice + AddMarketPrice(marketPrice MarketPrice) + getMarketPriceChan() <-chan MarketPrice +} + +type feed struct { + id string + marketPriceChan chan MarketPrice +} + +func NewFeed() (Feed, error) { + uuid, err := uuid.NewUUID() + if err != nil { + return nil, err + } + + return &feed{ + id: uuid.String(), + marketPriceChan: make(chan MarketPrice), + }, nil +} + +func (f feed) AddMarketPrice(marketPrice MarketPrice) { + f.marketPriceChan <- marketPrice +} + +func (f feed) getMarketPriceChan() <-chan MarketPrice { + return f.marketPriceChan } func merge(feeds ...Feed) <-chan MarketPrice { @@ -18,7 +47,7 @@ func merge(feeds ...Feed) <-chan MarketPrice { wg.Add(len(feeds)) for _, feed := range feeds { - c := feed.getMarketPriceFeed() + c := feed.getMarketPriceChan() go func(c <-chan MarketPrice) { for marketPrice := range c { mergedChan <- marketPrice From 4d591d300bfaafa9530c75233436456e1c51b914 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 10:53:06 +0100 Subject: [PATCH 03/70] go mod / sum --- go.mod | 1 + go.sum | 1 + 2 files changed, 2 insertions(+) diff --git a/go.mod b/go.mod index 0fcc774..d3d3ea9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/tdex-network/tdex-feeder go 1.15 require ( + github.com/google/uuid v1.1.2 github.com/gorilla/websocket v1.4.2 github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.2.2 diff --git a/go.sum b/go.sum index 6009be7..06dad2b 100644 --- a/go.sum +++ b/go.sum @@ -26,6 +26,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= From d218afca3487525ea2f6fc16d4c0fb444c326d0b Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 10:53:29 +0100 Subject: [PATCH 04/70] ports layer kraken web socket --- internal/ports/krakenWebSocket.go | 109 ++++++++++++++++++++++++++++++ internal/ports/types.go | 23 +++++++ 2 files changed, 132 insertions(+) create mode 100644 internal/ports/krakenWebSocket.go create mode 100644 internal/ports/types.go diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go new file mode 100644 index 0000000..688e98e --- /dev/null +++ b/internal/ports/krakenWebSocket.go @@ -0,0 +1,109 @@ +package ports + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "net/url" + "strconv" + + "github.com/gorilla/websocket" +) + +type KrakenWebSocket interface { + Connect(address string, tickersToSubscribe []string) error + Read() (*TickerWithPrice, error) + Close() error +} + +type krakenWebSocket struct { + connSocket *websocket.Conn +} + +func NewKrakenWebSocket() KrakenWebSocket { + return &krakenWebSocket{ + connSocket: nil, + } +} + +func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []string) error { + conn, err := connectToSocket(address) + if err != nil { + return err + } + + for _, ticker := range tickersToSubscribe { + msg := createSubscribeToMarketMessage(ticker) + msgBytes, err := json.Marshal(msg) + if err != nil { + return err + } + + err = conn.WriteMessage(websocket.TextMessage, msgBytes) + if err != nil { + return err + } + } + + socket.connSocket = conn + + return nil +} + +func (socket *krakenWebSocket) Close() error { + err := socket.connSocket.Close() + return err +} + +func (socket *krakenWebSocket) Read() (*TickerWithPrice, error) { + if socket.connSocket == nil { + return nil, errors.New("Socket not connected") + } + + var msgAsJson []interface{} + + _, message, err := socket.connSocket.ReadMessage() + if err != nil { + return nil, err + } + + err = json.Unmarshal([]byte(message), &msgAsJson) + if err != nil { + return nil, err + } + + if len(msgAsJson) < 4 { + return nil, errors.New("Invalid message" + fmt.Sprint(message)) + } + + pricesJson := msgAsJson[1].(map[string]interface{}) + priceAsk := pricesJson["c"].([]interface{}) + price, _ := strconv.ParseFloat(priceAsk[0].(string), 64) + + ticker := msgAsJson[3].(string) + + return &TickerWithPrice{ + ticker: ticker, + price: price, + }, nil +} + +// ConnectToSocket dials and returns a new client connection to a remote host +func connectToSocket(address string) (*websocket.Conn, error) { + u := url.URL{Scheme: "wss", Host: address, Path: "/"} + + c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + return c, err + } + log.Println("Connected to socket:", u.String()) + return c, nil +} + +// CreateSubscribeToMarketMessage gets a string with a market pair and returns +// a RequestMessage struct with instructions to subscrive to that market pair ticker. +func createSubscribeToMarketMessage(ticker string) requestMessage { + s := subscription{Name: "ticker"} + return requestMessage{"subscribe", []string{ticker}, &s, 0} +} \ No newline at end of file diff --git a/internal/ports/types.go b/internal/ports/types.go new file mode 100644 index 0000000..dcab7dd --- /dev/null +++ b/internal/ports/types.go @@ -0,0 +1,23 @@ +package ports + +type TickerWithPrice struct { + ticker string + price float64 +} + +type subscription struct { + Name string `json:"name"` + Interval int `json:"interval,omitempty"` + Token string `json:"token,omitempty"` + Depth int `json:"depth,omitempty"` + Snapshop bool `json:"snapshot,omitempty"` +} + +// RequestMessage is the data structure used to create +// jsons in order subscribe to market updates on Kraken +type requestMessage struct { + Event string `json:"event"` + Pair []string `json:"pair,omitempty"` + Subscription *subscription `json:"subscription,omitempty"` + Reqid int `json:"reqid,omitempty"` +} \ No newline at end of file From 00face7aa3def6ab9bc32690bf55a16ac50b1169 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 11:34:55 +0100 Subject: [PATCH 05/70] reset socketConn in KrakenWebSocket impl --- internal/ports/krakenWebSocket.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index 688e98e..d8ee3a3 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -53,6 +53,7 @@ func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []stri func (socket *krakenWebSocket) Close() error { err := socket.connSocket.Close() + socket.connSocket = nil return err } @@ -84,8 +85,8 @@ func (socket *krakenWebSocket) Read() (*TickerWithPrice, error) { ticker := msgAsJson[3].(string) return &TickerWithPrice{ - ticker: ticker, - price: price, + Ticker: ticker, + Price: price, }, nil } From 64e718ccb262ae5e709e11a9666455393144a751 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 11:35:10 +0100 Subject: [PATCH 06/70] Feed service for krakenWebSocket --- internal/application/feed_service.go | 76 ++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 internal/application/feed_service.go diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go new file mode 100644 index 0000000..34b0e8a --- /dev/null +++ b/internal/application/feed_service.go @@ -0,0 +1,76 @@ +package application + +import ( + log "github.com/sirupsen/logrus" + + "github.com/tdex-network/tdex-feeder/internal/domain" + "github.com/tdex-network/tdex-feeder/internal/ports" +) + +type FeedService interface { + Start() + Stop() + GetFeed() domain.Feed +} + +type krakenFeedService struct { + feed domain.Feed + krakenWebSocket ports.KrakenWebSocket + listening bool + tickersToMarketMap map[string]domain.Market +} + +func NewKrakenFeedService( + address string, + tickersToSubscribe []string, + tickersToMarketMap map[string]domain.Market, +) (FeedService, error) { + newFeed, err := domain.NewFeed() + if err != nil { + return nil, err + } + + krakenSocket := ports.NewKrakenWebSocket() + err = krakenSocket.Connect(address, tickersToSubscribe) + if err != nil { + return nil, err + } + + return &krakenFeedService{ + krakenWebSocket: krakenSocket, + feed: newFeed, + listening: false, + tickersToMarketMap: tickersToMarketMap, + }, nil +} + +func (f *krakenFeedService) GetFeed() domain.Feed { + return f.feed +} + +func (f *krakenFeedService) Start() { + for f.listening { + tickerWithPrice, err := f.krakenWebSocket.Read() + if err != nil { + log.Debug("Read message error: ", err) + } + + market, ok := f.tickersToMarketMap[tickerWithPrice.Ticker] + if !ok { + log.Debug("Market not found for ticker: ", tickerWithPrice.Ticker) + } + + f.feed.AddMarketPrice(domain.MarketPrice{ + Market: market, + Price: domain.Price{ + BasePrice: 1 / float32(tickerWithPrice.Price), + QuotePrice: float32(tickerWithPrice.Price), + }, + }) + } +} + +func (f *krakenFeedService) Stop() { + f.listening = false + f.krakenWebSocket.Close() +} \ No newline at end of file From 443396112052d3ed1fb1011d4237b82d613c63ac Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 11:35:28 +0100 Subject: [PATCH 07/70] export TicketWithPrice member in ports package --- internal/ports/types.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/ports/types.go b/internal/ports/types.go index dcab7dd..8359d04 100644 --- a/internal/ports/types.go +++ b/internal/ports/types.go @@ -1,8 +1,8 @@ package ports type TickerWithPrice struct { - ticker string - price float64 + Ticker string + Price float64 } type subscription struct { From 84ce449da62744e2cb81d4dfda67c46a9ded1a04 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 11:35:37 +0100 Subject: [PATCH 08/70] start feeder_service --- internal/application/feeder_service.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 internal/application/feeder_service.go diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go new file mode 100644 index 0000000..7e5dd17 --- /dev/null +++ b/internal/application/feeder_service.go @@ -0,0 +1,21 @@ +package application + +import "github.com/tdex-network/tdex-feeder/internal/domain" + +type FeederService interface { + Start() error + Stop() error +} + +type feederService struct { + tdexFeeder domain.TdexFeeder + krakenService krakenFeedService +} + +func (feeder *feederService) Start() error { + +} + +func (feeder *feederService) Stop() error { + +} \ No newline at end of file From d679ee71633a832a3494f627872de461cd96cc65 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 16:13:16 +0100 Subject: [PATCH 09/70] tdexDaemonPriceUpdater in ports package --- internal/ports/tdexDaemonPriceUpdater.go | 73 ++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 internal/ports/tdexDaemonPriceUpdater.go diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdexDaemonPriceUpdater.go new file mode 100644 index 0000000..3309b8c --- /dev/null +++ b/internal/ports/tdexDaemonPriceUpdater.go @@ -0,0 +1,73 @@ +package ports + +import ( + "context" + "errors" + + log "github.com/sirupsen/logrus" + + "github.com/tdex-network/tdex-feeder/internal/domain" + pboperator "github.com/tdex-network/tdex-protobuf/generated/go/operator" + "github.com/tdex-network/tdex-protobuf/generated/go/types" + "google.golang.org/grpc" +) + +type TdexDaemonPriceUpdater interface { + UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error +} + +func NewTdexDaemonPriceUpdater(ctx context.Context, operatorInterfaceEndpoint string) TdexDaemonPriceUpdater { + connGrpc, err := connectToGRPC(ctx, operatorInterfaceEndpoint) + if err != nil { + log.Fatal(err) + } + + operatorClient := pboperator.NewOperatorClient(connGrpc) + + return &tdexDaemonPriceUpdater{ + clientGRPC: operatorClient, + } +} + +type tdexDaemonPriceUpdater struct { + clientGRPC pboperator.OperatorClient +} + +func (updater *tdexDaemonPriceUpdater) UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error { + + if marketPrice.Price.BasePrice == 0.00 { + return errors.New("Base price is 0.00") + } + + if marketPrice.Price.BasePrice == 0.00 { + return errors.New("Quote price is 0.00") + } + + args := pboperator.UpdateMarketPriceRequest{ + Market: &types.Market{ + BaseAsset: marketPrice.Market.BaseAsset, + QuoteAsset: marketPrice.Market.QuoteAsset, + }, + Price: &types.Price{ + BasePrice: marketPrice.Price.BasePrice, + QuotePrice: marketPrice.Price.BasePrice, + }, + } + + _, err := updater.clientGRPC.UpdateMarketPrice(ctx, &args) + if err != nil { + return err + } + + return nil +} + +// ConnectTogRPC dials and returns a new client connection to a remote host +func connectToGRPC(ctx context.Context, daemonEndpoint string) (*grpc.ClientConn, error) { + conn, err := grpc.DialContext(ctx, daemonEndpoint, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + return conn, err + } + log.Println("Connected to gRPC:", daemonEndpoint) + return conn, nil +} \ No newline at end of file From 34053e98f8665389b979dd042494340a1b73198d Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 30 Nov 2020 21:40:58 +0100 Subject: [PATCH 10/70] add updater_service.go --- internal/application/updater_service.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 internal/application/updater_service.go diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go new file mode 100644 index 0000000..fd7687a --- /dev/null +++ b/internal/application/updater_service.go @@ -0,0 +1,25 @@ +package application + +import ( + "context" + + "github.com/tdex-network/tdex-feeder/internal/domain" + "github.com/tdex-network/tdex-feeder/internal/ports" +) + +// Implements the domain.Target interface +type TdexDaemonTarget struct { + Endpoint string + priceUpdater ports.TdexDaemonPriceUpdater +} + +func NewTdexDaemonTarget(tdexDaemonOperatorInterfaceEnpoint string) domain.Target { + return &TdexDaemonTarget{ + Endpoint: tdexDaemonOperatorInterfaceEnpoint, + priceUpdater: ports.NewTdexDaemonPriceUpdater(context.Background(), tdexDaemonOperatorInterfaceEnpoint), + } +} + +func (daemon *TdexDaemonTarget) Push(marketPrice domain.MarketPrice) error { + return daemon.priceUpdater.UpdateMarketPrice(context.Background(), marketPrice) +} \ No newline at end of file From cf2547bd066dbe25fb481729d982388e4fd8e8f3 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 08:41:41 +0100 Subject: [PATCH 11/70] change tickers to subcribe param for factory function --- internal/application/feed_service.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index 34b0e8a..d8a2fcb 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -22,7 +22,6 @@ type krakenFeedService struct { func NewKrakenFeedService( address string, - tickersToSubscribe []string, tickersToMarketMap map[string]domain.Market, ) (FeedService, error) { newFeed, err := domain.NewFeed() @@ -30,6 +29,11 @@ func NewKrakenFeedService( return nil, err } + tickersToSubscribe := make([]string, 0) + for k, _ := range tickersToMarketMap { + tickersToSubscribe = append(tickersToSubscribe, k) + } + krakenSocket := ports.NewKrakenWebSocket() err = krakenSocket.Connect(address, tickersToSubscribe) if err != nil { From 4195cc6a6159f2896f239be6a0b538651590190f Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 08:52:04 +0100 Subject: [PATCH 12/70] feeder service base implementation --- internal/application/feeder_service.go | 36 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go index 7e5dd17..bc51200 100644 --- a/internal/application/feeder_service.go +++ b/internal/application/feeder_service.go @@ -1,6 +1,9 @@ package application -import "github.com/tdex-network/tdex-feeder/internal/domain" +import ( + log "github.com/sirupsen/logrus" + "github.com/tdex-network/tdex-feeder/internal/domain" +) type FeederService interface { Start() error @@ -9,13 +12,38 @@ type FeederService interface { type feederService struct { tdexFeeder domain.TdexFeeder - krakenService krakenFeedService + krakenService FeedService } -func (feeder *feederService) Start() error { +type NewFeederServiceArgs struct { + OperatorEndpoint string + KrakenWSaddress string + TickerToMarket map[string]domain.Market +} + +func NewFeederService(args NewFeederServiceArgs) FeederService { + target := NewTdexDaemonTarget(args.OperatorEndpoint) + krakenFeedService, err := NewKrakenFeedService(args.KrakenWSaddress, args.TickerToMarket) + if err != nil { + log.Fatal(err) + } + + feeder := domain.NewTdexFeeder([]domain.Feed{krakenFeedService.GetFeed()}, []domain.Target{target}) + return &feederService{ + tdexFeeder: feeder, + krakenService: krakenFeedService, + } +} + +func (feeder *feederService) Start() error { + go feeder.krakenService.Start() + err := feeder.tdexFeeder.Start() + return err } func (feeder *feederService) Stop() error { - + feeder.krakenService.Stop() + feeder.tdexFeeder.Stop() + return nil } \ No newline at end of file From 791e8c9b751ecdeb035e13a738fc4c0c64198bcf Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 09:47:06 +0100 Subject: [PATCH 13/70] base implementation of config_service --- internal/adapters/config_service.go | 114 ++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 internal/adapters/config_service.go diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go new file mode 100644 index 0000000..6de3056 --- /dev/null +++ b/internal/adapters/config_service.go @@ -0,0 +1,114 @@ +package adapters + +import ( + "encoding/json" + "errors" + "regexp" + + "github.com/tdex-network/tdex-feeder/internal/application" + "github.com/tdex-network/tdex-feeder/internal/domain" +) + +type MarketJson struct { + BaseAsset string `json:"base_asset"` + QuoteAsset string `json:"quote_asset"` + KrakenTicker string `json:"kraken_ticker"` +} + +type ConfigJson struct { + DaemonEndpoint string `json:"daemon_endpoint"` + KrakenWsEndpoint string `json:"kraken_ws_endpoint"` + Markets []MarketJson `json:"markets"` +} + +type Config struct { + daemonEndpoint string + krakenWSaddress string + markets map[string]domain.Market +} + +func (config *Config) ToFeederService() application.FeederService { + feederSvc := application.NewFeederService(application.NewFeederServiceArgs{ + KrakenWSaddress: config.krakenWSaddress, + OperatorEndpoint: config.daemonEndpoint, + TickerToMarket: config.markets, + }) + + return feederSvc +} + +func (config *Config) UnmarshalJSON(data []byte) error { + jsonConfig := &ConfigJson{} + err := json.Unmarshal(data, jsonConfig) + if err != nil { + return err + } + + err = jsonConfig.validate() + if err != nil { + return err + } + + config.daemonEndpoint = jsonConfig.DaemonEndpoint + config.krakenWSaddress = jsonConfig.KrakenWsEndpoint + + configTickerToMarketMap := make(map[string]domain.Market) + + for _, marketJson := range jsonConfig.Markets { + configTickerToMarketMap[marketJson.KrakenTicker] = domain.Market{ + BaseAsset: marketJson.BaseAsset, + QuoteAsset: marketJson.QuoteAsset, + } + } + + config.markets = configTickerToMarketMap + + return nil +} + +func (configJson ConfigJson) validate() error { + if configJson.DaemonEndpoint == "" { + return errors.New("daemon endpoint is empty") + } + + if configJson.KrakenWsEndpoint == "" { + return errors.New("kraken websocket endpoint is empty") + } + + if len(configJson.Markets) == 0 { + return errors.New("need at least 1 market to feed") + } + + for _, marketJson := range configJson.Markets { + if marketJson.KrakenTicker == "" { + return errors.New("krakenTicker is empty") + } + + err := validateAssetString(marketJson.BaseAsset) + if err != nil { + return err + } + + err = validateAssetString(marketJson.QuoteAsset) + if err != nil { + return err + } + } + + return nil +} + +func validateAssetString(asset string) error { + const regularExpression = `[0-9A-Fa-f]{64}` + + matched, err := regexp.Match(regularExpression, []byte(asset)) + if err != nil { + return err + } + + if !matched { + return errors.New(asset + " is an invalid asset string.") + } + + return nil +} From 45cff3dedea9856bc1dfa22174ae9ba11f736891 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 09:50:31 +0100 Subject: [PATCH 14/70] delete old files --- cmd/feeder/main.go | 5 -- config/config.go | 96 ---------------------- config/types.go | 8 -- internal/application/feed_service.go | 2 +- pkg/conn/grpc.go | 59 -------------- pkg/conn/messages.go | 115 --------------------------- pkg/conn/socket.go | 21 ----- pkg/marketinfo/marketinfo.go | 25 ------ 8 files changed, 1 insertion(+), 330 deletions(-) delete mode 100644 cmd/feeder/main.go delete mode 100644 config/config.go delete mode 100644 config/types.go delete mode 100644 pkg/conn/grpc.go delete mode 100644 pkg/conn/messages.go delete mode 100644 pkg/conn/socket.go delete mode 100644 pkg/marketinfo/marketinfo.go diff --git a/cmd/feeder/main.go b/cmd/feeder/main.go deleted file mode 100644 index 7905807..0000000 --- a/cmd/feeder/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - -} diff --git a/config/config.go b/config/config.go deleted file mode 100644 index f97e77e..0000000 --- a/config/config.go +++ /dev/null @@ -1,96 +0,0 @@ -package config - -import ( - "encoding/json" - "errors" - "io/ioutil" - "os" - "reflect" - "strings" - - log "github.com/sirupsen/logrus" -) - -const ( - defaultDaemonEndpoint = "localhost:9000" - defaultKrakenWsEndpoint = "ws.kraken.com" -) - -// Config defines the struct for the configuration JSON file -type Config struct { - DaemonEndpoint string `json:"daemon_endpoint,required"` - DaemonMacaroon string `json:"daemon_macaroon"` - KrakenWsEndpoint string `json:"kraken_ws_endpoint,required"` - Markets []Market `json:"markets,required"` -} - -// DefaultConfig returns the datastructure needed -// for a default connection. -func defaultConfig() Config { - return Config{ - DaemonEndpoint: defaultDaemonEndpoint, - KrakenWsEndpoint: defaultKrakenWsEndpoint, - Markets: nil, - } -} - -// LoadConfigFromFile reads a file with the intended running behaviour -// and returns a Config struct with the respective configurations. -func loadConfigFromFile(filePath string) (Config, error) { - jsonFile, err := os.Open(filePath) - if err != nil { - return Config{}, err - } - defer jsonFile.Close() - - var config Config - - byteValue, err := ioutil.ReadAll(jsonFile) - if err != nil { - return Config{}, err - } - err = json.Unmarshal(byteValue, &config) - if err != nil { - return Config{}, err - } - err = checkConfigParsing(config) - if err != nil { - return Config{}, err - } - - return config, nil -} - -// checkConfigParsing checks if all the required fields -// were correctly loaded into the Config struct. -func checkConfigParsing(config Config) error { - fields := reflect.ValueOf(config) - for i := 0; i < fields.NumField(); i++ { - tags := fields.Type().Field(i).Tag - if strings.Contains(string(tags), "required") && fields.Field(i).IsZero() { - return errors.New("Config required field is missing: " + string(tags)) - } - } - for _, market := range config.Markets { - fields := reflect.ValueOf(market) - for i := 0; i < fields.NumField(); i++ { - tags := fields.Type().Field(i).Tag - if strings.Contains(string(tags), "required") && fields.Field(i).IsZero() { - return errors.New("Config required field is missing: " + string(tags)) - } - } - } - return nil -} - -// LoadConfig handles the default behaviour for loading -// config.json files. In case the file is not found, -// it loads the default config. -func LoadConfig(filePath string) (Config, error) { - _, err := os.Stat(filePath) - if os.IsNotExist(err) { - log.Debugf("File not found: %s. Loading default config.\n", filePath) - return defaultConfig(), nil - } - return loadConfigFromFile(filePath) -} diff --git a/config/types.go b/config/types.go deleted file mode 100644 index 246fee2..0000000 --- a/config/types.go +++ /dev/null @@ -1,8 +0,0 @@ -package config - -type Market struct { - BaseAsset string `json:"base_asset,required"` - QuoteAsset string `json:"quote_asset,required"` - KrakenTicker string `json:"kraken_ticker,required"` - Interval int `json:"interval,required"` -} diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index d8a2fcb..3448d20 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -30,7 +30,7 @@ func NewKrakenFeedService( } tickersToSubscribe := make([]string, 0) - for k, _ := range tickersToMarketMap { + for k := range tickersToMarketMap { tickersToSubscribe = append(tickersToSubscribe, k) } diff --git a/pkg/conn/grpc.go b/pkg/conn/grpc.go deleted file mode 100644 index aac8028..0000000 --- a/pkg/conn/grpc.go +++ /dev/null @@ -1,59 +0,0 @@ -package conn - -import ( - "context" - "time" - - log "github.com/sirupsen/logrus" - - "github.com/tdex-network/tdex-feeder/pkg/marketinfo" - pboperator "github.com/tdex-network/tdex-protobuf/generated/go/operator" - pbtypes "github.com/tdex-network/tdex-protobuf/generated/go/types" - "google.golang.org/grpc" -) - -const ( - timeout = 3 -) - -// ConnectTogRPC dials and returns a new client connection to a remote host -func ConnectTogRPC(daemonEndpoint string) (*grpc.ClientConn, error) { - ctx, cancel := context.WithTimeout(context.Background(), time.Second*timeout) - defer cancel() - conn, err := grpc.DialContext(ctx, daemonEndpoint, grpc.WithInsecure(), grpc.WithBlock()) - if err != nil { - return conn, err - } - log.Println("Connected to gRPC:", daemonEndpoint) - return conn, nil -} - -// UpdateMarketPricegRPC calls the tdex daemon UpdateMarketPrice rpc endpoint to update a defined market -func UpdateMarketPricegRPC(marketInfo marketinfo.MarketInfo, clientgRPC pboperator.OperatorClient) { - - if marketInfo.Price == 0.00 { - log.Println("Can't send gRPC request with no price") - return - } - - log.Printf("%s %g for market %s-%s", marketInfo.Config.KrakenTicker, marketInfo.Price, marketInfo.Config.BaseAsset[:4], marketInfo.Config.QuoteAsset[:4]) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second) - defer cancel() - - _, err := clientgRPC.UpdateMarketPrice(ctx, &pboperator.UpdateMarketPriceRequest{ - Market: &pbtypes.Market{ - BaseAsset: marketInfo.Config.BaseAsset, - QuoteAsset: marketInfo.Config.QuoteAsset, - }, - Price: &pbtypes.Price{ - BasePrice: 1 / float32(marketInfo.Price), - QuotePrice: float32(marketInfo.Price), - }, - }) - - if err != nil { - log.Println(err) - return - } -} diff --git a/pkg/conn/messages.go b/pkg/conn/messages.go deleted file mode 100644 index 308701b..0000000 --- a/pkg/conn/messages.go +++ /dev/null @@ -1,115 +0,0 @@ -package conn - -import ( - "encoding/json" - "strconv" - "time" - - "github.com/gorilla/websocket" - log "github.com/sirupsen/logrus" - - "github.com/tdex-network/tdex-feeder/pkg/marketinfo" - "github.com/tdex-network/tdex-protobuf/generated/go/operator" -) - -const ( - nanoToSeconds = 1000000000 -) - -// RequestMessage is the data structure used to create -// jsons in order subscribe to market updates on Kraken -type RequestMessage struct { - Event string `json:"event"` - Pair []string `json:"pair,omitempty"` - Subscription *Subscription `json:"subscription,omitempty"` - Reqid int `json:"reqid,omitempty"` -} - -type Subscription struct { - Name string `json:"name"` - Interval int `json:"interval,omitempty"` - Token string `json:"token,omitempty"` - Depth int `json:"depth,omitempty"` - Snapshop bool `json:"snapshot,omitempty"` -} - -// CreatePingMessage returns a RequestMessage struct -// with a ping Event. -func CreatePingMessage() RequestMessage { - return RequestMessage{Event: "ping"} -} - -// CreateSubscribeToMarketMessage gets a string with a market pair and returns -// a RequestMessage struct with instructions to subscrive to that market pair ticker. -func CreateSubscribeToMarketMessage(marketpair string) RequestMessage { - s := Subscription{Name: "ticker"} - return RequestMessage{"subscribe", []string{marketpair}, &s, 0} -} - -// SendRequestMessage gets a socket connection and a RequestMessage struct, -// marshalls the struct and sends the message using the socket. -func SendRequestMessage(c *websocket.Conn, m RequestMessage) error { - b, err := json.Marshal(m) - if err != nil { - return err - } - err = c.WriteMessage(websocket.TextMessage, []byte(b)) - if err != nil { - return err - } - return nil -} - -// HandleMessages is responsible for the perpetual loop of receiving messages -// from subscriptions, retrieving the price from them and send the gRPC request -// to update the market price in the predeterminated interval. -func HandleMessages(done chan string, cSocket *websocket.Conn, marketsInfos []marketinfo.MarketInfo, clientgRPC operator.OperatorClient) { - defer close(done) - for { - _, message, err := cSocket.ReadMessage() - if err != nil { - log.Debug("Message Error:", err) - return - } - log.Debug(string(message)) - marketsInfos = retrievePriceFromMessage(message, marketsInfos) - marketsInfos = checkInterval(marketsInfos, clientgRPC) - } -} - -// checkInterval handles the gRPC calls for UpdateMarketPrice -// at a predeterminated inteval for each market. -func checkInterval(marketsInfos []marketinfo.MarketInfo, clientgRPC operator.OperatorClient) []marketinfo.MarketInfo { - for i, marketInfo := range marketsInfos { - elapsedSeconds := time.Since(marketInfo.LastSent).Round(time.Second) - marketInterval := time.Duration(marketInfo.Config.Interval * int(nanoToSeconds)) - if elapsedSeconds == marketInterval { - UpdateMarketPricegRPC(marketInfo, clientgRPC) - marketInfo.LastSent = time.Now() - marketsInfos[i] = marketInfo - } - } - return marketsInfos -} - -// retrievePriceFromMessage gets a message from a subscription and retrieves the -// price information, updating the price of the specific market. -func retrievePriceFromMessage(message []byte, marketsInfos []marketinfo.MarketInfo) []marketinfo.MarketInfo { - var result []interface{} - err := json.Unmarshal([]byte(message), &result) - if err != nil { - return marketsInfos - } - if len(result) == 4 { - pricesJson := result[1].(map[string]interface{}) - priceAsk := pricesJson["c"].([]interface{}) - price, _ := strconv.ParseFloat(priceAsk[0].(string), 64) - for i, marketInfo := range marketsInfos { - if marketInfo.Config.KrakenTicker == result[3] { - marketInfo.Price = price - marketsInfos[i] = marketInfo - } - } - } - return marketsInfos -} diff --git a/pkg/conn/socket.go b/pkg/conn/socket.go deleted file mode 100644 index 54151f5..0000000 --- a/pkg/conn/socket.go +++ /dev/null @@ -1,21 +0,0 @@ -package conn - -import ( - "net/url" - - log "github.com/sirupsen/logrus" - - "github.com/gorilla/websocket" -) - -// ConnectToSocket dials and returns a new client connection to a remote host -func ConnectToSocket(address string) (*websocket.Conn, error) { - u := url.URL{Scheme: "wss", Host: address, Path: "/"} - - c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) - if err != nil { - return c, err - } - log.Println("Connected to socket:", u.String()) - return c, nil -} diff --git a/pkg/marketinfo/marketinfo.go b/pkg/marketinfo/marketinfo.go deleted file mode 100644 index 52086b4..0000000 --- a/pkg/marketinfo/marketinfo.go +++ /dev/null @@ -1,25 +0,0 @@ -package marketinfo - -import ( - "time" - - "github.com/tdex-network/tdex-feeder/config" -) - -// MarketInfo stores the informations necessary for -// handling different market pair prices in real-time. -type MarketInfo struct { - Config config.Market - LastSent time.Time - Price float64 -} - -// InitialMarketInfo returns a pointer to a MarketInfo struct -// with the default configurations. -func InitialMarketInfo(market config.Market) MarketInfo { - return MarketInfo{ - Config: market, - LastSent: time.Now(), - Price: 0, - } -} From 46245fa30e3edad97c5d35ca51c3bf499573aab5 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 10:07:58 +0100 Subject: [PATCH 15/70] base implementation main --- cmd/feederd/main.go | 108 ++++++++++---------------------------------- 1 file changed, 25 insertions(+), 83 deletions(-) diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index f32b37b..7ef7439 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -4,114 +4,56 @@ package main import ( - "flag" + "encoding/json" + "io/ioutil" "os" "os/signal" "time" log "github.com/sirupsen/logrus" - "google.golang.org/grpc" - - "github.com/gorilla/websocket" - "github.com/tdex-network/tdex-feeder/config" - "github.com/tdex-network/tdex-feeder/pkg/conn" - "github.com/tdex-network/tdex-feeder/pkg/marketinfo" - - pboperator "github.com/tdex-network/tdex-protobuf/generated/go/operator" + "github.com/tdex-network/tdex-feeder/internal/adapters" + "github.com/tdex-network/tdex-feeder/internal/application" ) const ( + envConfigPathKey = "TDEX_FEEDER_CONFIG_PATH" defaultConfigPath = "./config.json" ) func main() { - interrupt, cSocket, marketsInfos, conngRPC := setup() - infiniteLoops(interrupt, cSocket, marketsInfos, conngRPC) -} - -func setup() (chan os.Signal, *websocket.Conn, []marketinfo.MarketInfo, *grpc.ClientConn) { - conf := checkFlags() - // Interrupt Notification. interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) - // Dials the connection the the Socket. - cSocket, err := conn.ConnectToSocket(conf.KrakenWsEndpoint) - if err != nil { - log.Fatal("Socket Connection Error: ", err) - } - marketsInfos := loadMarkets(conf, cSocket) - if len(marketsInfos) == 0 { - log.Warn("list of market to feed is empty") + // retrieve feeder service from config file + envConfigPath := os.Getenv(envConfigPathKey) + if envConfigPath == "" { + envConfigPath = defaultConfigPath } + feeder := configFileToFeederService(envConfigPath) - // Set up the connection to the gRPC server. - conngRPC, err := conn.ConnectTogRPC(conf.DaemonEndpoint) - if err != nil { - log.Fatal("gRPC Connection Error: ", err) + + go feeder.Start() + + // check for interupt + for range interrupt { + log.Println("Shutting down the feeder") + time.Sleep(time.Second) + feeder.Stop() } - return interrupt, cSocket, marketsInfos, conngRPC + } -// Checks for command line flags for Config Path and Debug mode. -// Loads flags as required. -func checkFlags() config.Config { - confFlag := flag.String("conf", defaultConfigPath, "Configuration File Path") - debugFlag := flag.Bool("debug", false, "Log Debug Informations") - flag.Parse() - if *debugFlag == true { - log.SetLevel(log.DebugLevel) - } - // Loads Config File. - conf, err := config.LoadConfig(*confFlag) +func configFileToFeederService(configFilePath string) application.FeederService { + configBytes, err := ioutil.ReadAll(configFilePath) if err != nil { log.Fatal(err) } - return conf -} -// Loads Config Markets infos into Data Structure and Subscribes to -// Messages from this Markets. -func loadMarkets(conf config.Config, cSocket *websocket.Conn) []marketinfo.MarketInfo { - numberOfMarkets := len(conf.Markets) - marketsInfos := make([]marketinfo.MarketInfo, numberOfMarkets) - for i, marketConfig := range conf.Markets { - marketsInfos[i] = marketinfo.InitialMarketInfo(marketConfig) - m := conn.CreateSubscribeToMarketMessage(marketConfig.KrakenTicker) - err := conn.SendRequestMessage(cSocket, m) - if err != nil { - log.Fatal("Couldn't send request message: ", err) - } - } - return marketsInfos -} + config := &adapters.Config{} + err = json.Unmarshal(configBytes, config) -func infiniteLoops(interrupt chan os.Signal, cSocket *websocket.Conn, marketsInfos []marketinfo.MarketInfo, conngRPC *grpc.ClientConn) { - defer cSocket.Close() - defer conngRPC.Close() - clientgRPC := pboperator.NewOperatorClient(conngRPC) - done := make(chan string) - // Handles Messages from subscriptions. Will periodically call the - // gRPC UpdateMarketPrice with the price info from the messages. - go conn.HandleMessages(done, cSocket, marketsInfos, clientgRPC) - checkInterrupt(interrupt, cSocket, done) + feeder := config.ToFeederService() + return feeder } -// Loop to keep cycle alive. Waits Interrupt to close the connection. -func checkInterrupt(interrupt chan os.Signal, cSocket *websocket.Conn, done chan string) { - for { - for range interrupt { - log.Println("Shutting down Feeder") - err := cSocket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - if err != nil { - log.Fatal("write close:", err) - } - select { - case <-done: - case <-time.After(time.Second): - } - return - } - } -} From ab92856383610d6fd4620c5b26186d133566e828 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 11:50:18 +0100 Subject: [PATCH 16/70] main test --- .gitignore | 3 + cmd/feederd/main.go | 26 +++- cmd/feederd/main_test.go | 212 +++++++++++++++++++++++++++ internal/application/feed_service.go | 1 + 4 files changed, 238 insertions(+), 4 deletions(-) create mode 100644 cmd/feederd/main_test.go diff --git a/.gitignore b/.gitignore index 09b8dc3..b11c642 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,6 @@ # Dependency directories (remove the comment below to include it) # vendor/ build/ + +cmd/feederd/tdexd +cmd/feederd/config.json \ No newline at end of file diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index 7ef7439..0672bbb 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "os/signal" + "syscall" "time" log "github.com/sirupsen/logrus" @@ -23,7 +24,7 @@ const ( func main() { // Interrupt Notification. interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt) + signal.Notify(interrupt, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) // retrieve feeder service from config file envConfigPath := os.Getenv(envConfigPathKey) @@ -33,25 +34,42 @@ func main() { feeder := configFileToFeederService(envConfigPath) - go feeder.Start() + go func () { + err := feeder.Start() + if err != nil { + log.Fatal(err) + } + }() // check for interupt for range interrupt { log.Println("Shutting down the feeder") time.Sleep(time.Second) - feeder.Stop() + err := feeder.Stop() + if err != nil { + log.Fatal(err) + } } } func configFileToFeederService(configFilePath string) application.FeederService { - configBytes, err := ioutil.ReadAll(configFilePath) + jsonFile, err := os.Open(configFilePath) + if err != nil { + log.Fatal(err) + } + defer jsonFile.Close() + + configBytes, err := ioutil.ReadAll(jsonFile) if err != nil { log.Fatal(err) } config := &adapters.Config{} err = json.Unmarshal(configBytes, config) + if err != nil { + log.Fatal(err) + } feeder := config.ToFeederService() return feeder diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go new file mode 100644 index 0000000..7bafff1 --- /dev/null +++ b/cmd/feederd/main_test.go @@ -0,0 +1,212 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/exec" + "testing" + "time" + + "github.com/tdex-network/tdex-feeder/internal/adapters" +) + +const ( + containerName = "tdexFeederContainerTest" + daemonEndpoint = "http://127.0.0.1:9000" + krakenWsEndpoint = "ws.kraken.com" + nigiriUrl = "https://nigiri.network/liquid/api" + password = "vulpemsecret" +) + +func TestFeeder(t *testing.T) { + runDaemonAndInitConfigFile(t) + t.Cleanup(stopAndDeleteContainer) + + t.Run("should feed the market using kraken feed", func(t *testing.T) { + go main() + time.Sleep(30 * time.Second) + os.Exit(0) + }) +} + +func runDaemonAndInitConfigFile(t *testing.T) { + usdt := runDaemonAndCreateMarket(t) + + configJson := adapters.ConfigJson{ + DaemonEndpoint: daemonEndpoint, + KrakenWsEndpoint: krakenWsEndpoint, + Markets: []adapters.MarketJson{ + adapters.MarketJson{ + KrakenTicker: "LTC/USDT", + BaseAsset: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", + QuoteAsset: usdt, + }, + }, + } + + bytes, err := json.Marshal(configJson) + if err != nil { + t.Error(err) + } + + err = ioutil.WriteFile(defaultConfigPath, bytes, os.ModePerm) + if err != nil { + t.Error(err) + } +} + +func runDaemonAndCreateMarket(t *testing.T) string { + _, err := execute( + "docker", "run", "--name", containerName, + "-p", "9000:9000", + "-d", + "-v", "tdexd:/.tdex-daemon", + "-e", "TDEX_NETWORK=regtest", + "-e", "TDEX_EXPLORER_ENDPOINT=" + nigiriUrl, + "-e", "TDEX_FEE_ACCOUNT_BALANCE_TRESHOLD=1000", + "-e", "TDEX_BASE_ASSET=5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", + "-e", "TDEX_LOG_LEVEL=5", + "tdexd:latest", + ) + + if err != nil { + t.Error(err) + } + + time.Sleep(5 * time.Second) + + _, err = runCLICommand("config", "init") + if err != nil { + t.Error(err) + } + + // init the wallet + seed, err := runCLICommand("genseed") + if err != nil { + t.Error(err) + } + + _, err = runCLICommand("init", "--seed", seed, "--password", password) + if err != nil { + t.Error(err) + } + + _, err = runCLICommand("unlock", "--password", password) + if err != nil { + t.Error(err) + } + + 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) + if err != nil { + t.Error(t, err) + } + + address := depositMarketResult["address"].(string) + usdt := fundMarketAddress(t, address) + + return usdt +} + +func stopAndDeleteContainer() { + _, err := execute("docker", "stop", containerName) + if err != nil { + panic(err) + } + + _, err = execute("docker", "container", "rm", containerName) + if err != nil { + panic(err) + } +} + +func fundMarketAddress(t *testing.T, address string) string { + _, err := faucet(address) + if err != nil { + t.Error(err) + } + + _, shitcoin, err := mint(address, 100) + if err != nil { + t.Error(err) + } + + time.Sleep(3 * time.Second) + return shitcoin +} + +func mint(address string, amount int) (string, string, error) { + 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)) + if err != nil { + return "", "", err + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", "", err + } + respBody := map[string]interface{}{} + err = json.Unmarshal(data, &respBody) + if err != nil { + return "", "", err + } + + return respBody["txId"].(string), respBody["asset"].(string), nil +} + +func faucet(address string) (string, error) { + 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)) + if err != nil { + return "", err + } + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", err + } + respBody := map[string]string{} + err = json.Unmarshal(data, &respBody) + if err != nil { + return "", err + } + + return respBody["txId"], nil +} + + +func execute(command string, args ...string) (string, error) { + cmd := exec.Command(command, args...) + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + err := cmd.Run() + result := out.String() + if err == nil { + return result, nil + } + + return out.String(), errors.New(fmt.Sprint(err) + ": " + stderr.String()) +} + +func runCLICommand(cliCommand string, args ...string) (string, error) { + commandArgs := []string{"exec", containerName, "tdex", cliCommand} + commandArgs = append(commandArgs, args...) + output, err := execute("docker", commandArgs...) + return output, err +} diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index 3448d20..ab27ed0 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -53,6 +53,7 @@ func (f *krakenFeedService) GetFeed() domain.Feed { } func (f *krakenFeedService) Start() { + log.Println("Start listening kraken service") for f.listening { tickerWithPrice, err := f.krakenWebSocket.Read() if err != nil { From 32f6d126df3ae3378e1327eb5c8b26d4613ff630 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 13:56:09 +0100 Subject: [PATCH 17/70] handle close error --- internal/application/feed_service.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index ab27ed0..6bd42af 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -77,5 +77,10 @@ func (f *krakenFeedService) Start() { func (f *krakenFeedService) Stop() { f.listening = false - f.krakenWebSocket.Close() + err := f.krakenWebSocket.Close() + if err != nil { + log.Fatal(err) + } + + log.Info("Feed service stopped") } \ No newline at end of file From 998729293b0cc380f8763f364fbd8f230bee7065 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 13:56:31 +0100 Subject: [PATCH 18/70] improve interupt --- cmd/feederd/main.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index 0672bbb..aad6f67 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -8,8 +8,6 @@ import ( "io/ioutil" "os" "os/signal" - "syscall" - "time" log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/adapters" @@ -24,7 +22,7 @@ const ( func main() { // Interrupt Notification. interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + signal.Notify(interrupt, os.Interrupt) // retrieve feeder service from config file envConfigPath := os.Getenv(envConfigPathKey) @@ -34,6 +32,7 @@ func main() { feeder := configFileToFeederService(envConfigPath) + log.Info("Start the feeder") go func () { err := feeder.Start() if err != nil { @@ -42,15 +41,14 @@ func main() { }() // check for interupt - for range interrupt { - log.Println("Shutting down the feeder") - time.Sleep(time.Second) - err := feeder.Stop() - if err != nil { - log.Fatal(err) - } + <-interrupt + log.Info("Shutting down the feeder") + err := feeder.Stop() + log.Info("Feeder service stopped") + if err != nil { + log.Fatal(err) } - + os.Exit(0) } func configFileToFeederService(configFilePath string) application.FeederService { From e92f820315236a15f037d4d5d86bf203a39fd686 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 13:56:57 +0100 Subject: [PATCH 19/70] change logger in krakenWebSocket.go --- internal/ports/krakenWebSocket.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index d8ee3a3..6650fac 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -4,10 +4,11 @@ import ( "encoding/json" "errors" "fmt" - "log" "net/url" "strconv" + log "github.com/sirupsen/logrus" + "github.com/gorilla/websocket" ) @@ -52,7 +53,7 @@ func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []stri } func (socket *krakenWebSocket) Close() error { - err := socket.connSocket.Close() + err := socket.connSocket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) socket.connSocket = nil return err } @@ -98,7 +99,7 @@ func connectToSocket(address string) (*websocket.Conn, error) { if err != nil { return c, err } - log.Println("Connected to socket:", u.String()) + log.Info("Connected to socket:", u.String()) return c, nil } From da13161725ab2e1f01a8abdd789e681c8560e5c5 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 13:57:05 +0100 Subject: [PATCH 20/70] feeder domain test --- internal/domain/feeder_test.go | 74 ++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 internal/domain/feeder_test.go diff --git a/internal/domain/feeder_test.go b/internal/domain/feeder_test.go new file mode 100644 index 0000000..caa25a4 --- /dev/null +++ b/internal/domain/feeder_test.go @@ -0,0 +1,74 @@ +package domain + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestFeeder(t *testing.T) { + feed, err := NewFeed() + if err != nil { + t.Error(err) + } + + feedBis, err := NewFeed() + if err != nil { + t.Error(err) + } + + target := &mockTarget{ + marketPrices: make([]MarketPrice, 0), + } + + + feeder := NewTdexFeeder([]Feed{feed, feedBis}, []Target{target}) + + t.Run("should push the feeds data to target", func(t *testing.T) { + marketPrice := MarketPrice{ + Market: Market{ + BaseAsset: "1111", + QuoteAsset: "0000", + }, + Price: Price{ + BasePrice: 0.2, + QuotePrice: 1, + }, + } + + go func () { + err := feeder.Start() + if err != nil { + t.Error(err) + } + }() + + time.Sleep(time.Second) + assert.Equal(t, true, feeder.IsRunning()) + + go func () { + for i := 0; i < 5; i++ { + feedBis.AddMarketPrice(marketPrice) + } + }() + + for i := 0; i < 10; i++ { + feed.AddMarketPrice(marketPrice) + } + + time.Sleep(500 * time.Millisecond) + + assert.Equal(t, 15, len(target.marketPrices)) + }) +} + +type mockTarget struct { + marketPrices []MarketPrice +} + +func (t *mockTarget) Push(marketPrice MarketPrice) error { + t.marketPrices = append(t.marketPrices, marketPrice) + return nil +} + From 04c41f1111e9b26fe25e7d1d7464c8f877e71c4b Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 15:21:53 +0100 Subject: [PATCH 21/70] fix message checks in krakenWebSocket.go --- internal/ports/krakenWebSocket.go | 38 +++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index 6650fac..f1d968b 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -34,7 +34,25 @@ func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []stri return err } + var connectMsg map[string]interface{} + + _, msg, err := conn.ReadMessage() + if err != nil { + return err + } + + err = json.Unmarshal(msg, &connectMsg) + if err != nil { + return err + } + + if status, ok := connectMsg["status"]; !ok || status != "online" { + return errors.New("Error connection with: " + fmt.Sprint(address)) + } + for _, ticker := range tickersToSubscribe { + var subscribeMsg map[string]interface{} + msg := createSubscribeToMarketMessage(ticker) msgBytes, err := json.Marshal(msg) if err != nil { @@ -45,6 +63,20 @@ func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []stri if err != nil { return err } + + _, subscribeMessage, err := conn.ReadMessage() + if err != nil { + return err + } + + err = json.Unmarshal(subscribeMessage, &subscribeMsg) + if err != nil { + return err + } + + if status, ok := subscribeMsg["status"]; !ok || status != "subscribed" { + return errors.New("Error subscribe to: " + fmt.Sprint(ticker)) + } } socket.connSocket = conn @@ -64,13 +96,15 @@ func (socket *krakenWebSocket) Read() (*TickerWithPrice, error) { } var msgAsJson []interface{} - _, message, err := socket.connSocket.ReadMessage() if err != nil { return nil, err } - err = json.Unmarshal([]byte(message), &msgAsJson) + msg := string(message) + log.Info(msg) + + err = json.Unmarshal([]byte(msg), &msgAsJson) if err != nil { return nil, err } From 06e465742eb803a9ea1011f447be775f75224572 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 15:22:04 +0100 Subject: [PATCH 22/70] krakenWebSocket tests --- internal/ports/krakenWebSocket_test.go | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 internal/ports/krakenWebSocket_test.go diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/krakenWebSocket_test.go new file mode 100644 index 0000000..2f14675 --- /dev/null +++ b/internal/ports/krakenWebSocket_test.go @@ -0,0 +1,38 @@ +package ports + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +const ( + address = "ws.kraken.com" + ticker = "LTC/USDT" +) + +func createAndConnect() (KrakenWebSocket, error) { + krakenWS := NewKrakenWebSocket() + err := krakenWS.Connect(address, []string{ticker}) + return krakenWS, err +} + +func TestConnectToKrakenWebSocket(t *testing.T) { + t.Run("should connect to a valid kraken web socket address", func(t *testing.T) { + _, err := createAndConnect() + assert.Nil(t, err) + }) +} + +func TestRead(t *testing.T) { + t.Run("should read marketInfo from websocket kraken stream", func(t *testing.T) { + ws, err := createAndConnect() + if err != nil { + t.Error(err) + } + + tickerWithPrice, err := ws.Read() + assert.Nil(t, err) + assert.NotNil(t, tickerWithPrice) + }) +} \ No newline at end of file From 5e961097b9404b002565f5619da8654bbf2ed1a3 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 17:02:44 +0100 Subject: [PATCH 23/70] create the chan in constructor function --- internal/domain/tdexFeeder.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go index 5a04191..1d539c2 100644 --- a/internal/domain/tdexFeeder.go +++ b/internal/domain/tdexFeeder.go @@ -20,6 +20,8 @@ func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { return &tdexFeeder{ feeds: feeds, targets: targets, + stopChan: make(chan bool), + running: false, } } From f47b44bacf454605dbf4e16e6149b7e688c511a6 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 17:03:06 +0100 Subject: [PATCH 24/70] stop the feeder in feeder test --- internal/domain/feeder_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/domain/feeder_test.go b/internal/domain/feeder_test.go index caa25a4..597d9b5 100644 --- a/internal/domain/feeder_test.go +++ b/internal/domain/feeder_test.go @@ -58,6 +58,7 @@ func TestFeeder(t *testing.T) { } time.Sleep(500 * time.Millisecond) + feeder.Stop() assert.Equal(t, 15, len(target.marketPrices)) }) From 6320df614535ad38918747c3061be9dfeb692802 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 17:03:25 +0100 Subject: [PATCH 25/70] add sleep time between two socket read --- internal/application/feed_service.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index 6bd42af..96a8370 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -1,6 +1,8 @@ package application import ( + "time" + log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" @@ -55,6 +57,7 @@ func (f *krakenFeedService) GetFeed() domain.Feed { func (f *krakenFeedService) Start() { log.Println("Start listening kraken service") for f.listening { + time.Sleep(500 * time.Millisecond) tickerWithPrice, err := f.krakenWebSocket.Read() if err != nil { log.Debug("Read message error: ", err) From 28174874af52595448e9f784d9f7bacb71ba5c9c Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 17:31:05 +0100 Subject: [PATCH 26/70] switch to quitChan pattern in FeedService.go --- internal/application/feed_service.go | 59 ++++++++++++++++------------ 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index 96a8370..c539b70 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -18,7 +18,7 @@ type FeedService interface { type krakenFeedService struct { feed domain.Feed krakenWebSocket ports.KrakenWebSocket - listening bool + stopChan chan bool tickersToMarketMap map[string]domain.Market } @@ -45,7 +45,7 @@ func NewKrakenFeedService( return &krakenFeedService{ krakenWebSocket: krakenSocket, feed: newFeed, - listening: false, + stopChan: make(chan bool), tickersToMarketMap: tickersToMarketMap, }, nil } @@ -55,35 +55,42 @@ func (f *krakenFeedService) GetFeed() domain.Feed { } func (f *krakenFeedService) Start() { + listening := true log.Println("Start listening kraken service") - for f.listening { - time.Sleep(500 * time.Millisecond) - tickerWithPrice, err := f.krakenWebSocket.Read() - if err != nil { - log.Debug("Read message error: ", err) - } + for listening { + select { + case <-f.stopChan: + listening = false + err := f.krakenWebSocket.Close() + if err != nil { + log.Fatal(err) + } - market, ok := f.tickersToMarketMap[tickerWithPrice.Ticker] - if !ok { - log.Debug("Market not found for ticker: ", tickerWithPrice.Ticker) - } + log.Info("Feed service stopped") + break; + case <-time.After(500 * time.Millisecond): + tickerWithPrice, err := f.krakenWebSocket.Read() + if err != nil { + log.Debug("Read message error: ", err) + continue + } - f.feed.AddMarketPrice(domain.MarketPrice{ - Market: market, - Price: domain.Price{ - BasePrice: 1 / float32(tickerWithPrice.Price), - QuotePrice: float32(tickerWithPrice.Price), - }, - }) + market, ok := f.tickersToMarketMap[tickerWithPrice.Ticker] + if !ok { + log.Debug("Market not found for ticker: ", tickerWithPrice.Ticker) + } + + f.feed.AddMarketPrice(domain.MarketPrice{ + Market: market, + Price: domain.Price{ + BasePrice: 1 / float32(tickerWithPrice.Price), + QuotePrice: float32(tickerWithPrice.Price), + }, + }) + } } } func (f *krakenFeedService) Stop() { - f.listening = false - err := f.krakenWebSocket.Close() - if err != nil { - log.Fatal(err) - } - - log.Info("Feed service stopped") + f.stopChan <- true } \ No newline at end of file From 02f314abd0d58b4ca832465236853e2ae3e84ae8 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 17:33:36 +0100 Subject: [PATCH 27/70] feed service test --- internal/application/feed_service_test.go | 59 +++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 internal/application/feed_service_test.go diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go new file mode 100644 index 0000000..9ba8560 --- /dev/null +++ b/internal/application/feed_service_test.go @@ -0,0 +1,59 @@ +package application + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/tdex-network/tdex-feeder/internal/domain" +) + +const ( + baseAsset = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225" + quoteAsset = "a64b14f3de72bc602d0786e6f034720a879a6b9339d59b09ddd49e1783ed227a" + krakenTicker = "LTC/USDT" + krakenWsEndpoint = "ws.kraken.com" +) + + + +func TestKrakenFeedService(t *testing.T) { + tickerMap := make(map[string]domain.Market) + tickerMap[krakenTicker] = domain.Market{ + BaseAsset: baseAsset, + QuoteAsset: quoteAsset, + } + + t.Run("Start a service should feed the Feed instance", func(t *testing.T) { + svc, err := NewKrakenFeedService(krakenWsEndpoint, tickerMap) + if err != nil { + t.Error(err) + } + go svc.Start() + defer svc.Stop() + + feed := svc.GetFeed() + target := &mockTarget{marketPrices: []domain.MarketPrice{}} + feeder := domain.NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) + go func() { + err := feeder.Start() + if err != nil { + t.Error(err) + } + }() + + time.Sleep(10 * time.Second) + feeder.Stop() + + assert.Equal(t, true, len(target.marketPrices) > 0) + }) +} + +type mockTarget struct { + marketPrices []domain.MarketPrice +} + +func (t *mockTarget) Push(marketPrice domain.MarketPrice) error { + t.marketPrices = append(t.marketPrices, marketPrice) + return nil +} From 08568ec0542dac22742d07f149c5acdd5b326659 Mon Sep 17 00:00:00 2001 From: louisinger Date: Tue, 1 Dec 2020 18:00:47 +0100 Subject: [PATCH 28/70] change nigiri endpoint explorer address --- cmd/feederd/main_test.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index 7bafff1..ac1e64b 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -17,9 +17,10 @@ import ( const ( containerName = "tdexFeederContainerTest" - daemonEndpoint = "http://127.0.0.1:9000" + daemonEndpoint = "127.0.0.1:9000" krakenWsEndpoint = "ws.kraken.com" - nigiriUrl = "https://nigiri.network/liquid/api" + // nigiriUrl = "https://nigiri.network/liquid/api" + nigiriUrl = "http://localhost:3001" password = "vulpemsecret" ) @@ -28,9 +29,9 @@ func TestFeeder(t *testing.T) { t.Cleanup(stopAndDeleteContainer) t.Run("should feed the market using kraken feed", func(t *testing.T) { - go main() - time.Sleep(30 * time.Second) - os.Exit(0) + main() + // time.Sleep(30 * time.Second) + // os.Exit(0) }) } @@ -154,6 +155,11 @@ func mint(address string, amount int) (string, string, error) { if err != nil { return "", "", err } + + if resp.StatusCode != 200 { + return "", "", errors.New("Internal server error") + } + data, err := ioutil.ReadAll(resp.Body) if err != nil { return "", "", err @@ -164,6 +170,10 @@ func mint(address string, amount int) (string, string, error) { return "", "", err } + if respBody["asset"].(string) == "" { + return mint(address, amount) + } + return respBody["txId"].(string), respBody["asset"].(string), nil } From cb92c93307169077eb9927afb01cde0c46f57c1c Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 10:17:15 +0100 Subject: [PATCH 29/70] main_test pass --- cmd/feederd/main_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index ac1e64b..4403d66 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -25,13 +25,17 @@ const ( ) func TestFeeder(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + runDaemonAndInitConfigFile(t) t.Cleanup(stopAndDeleteContainer) t.Run("should feed the market using kraken feed", func(t *testing.T) { - main() - // time.Sleep(30 * time.Second) - // os.Exit(0) + go main() + time.Sleep(30 * time.Second) + os.Exit(0) }) } @@ -72,6 +76,7 @@ func runDaemonAndCreateMarket(t *testing.T) string { "-e", "TDEX_FEE_ACCOUNT_BALANCE_TRESHOLD=1000", "-e", "TDEX_BASE_ASSET=5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", "-e", "TDEX_LOG_LEVEL=5", + "--network=host", "tdexd:latest", ) From b27824f3329045d4412e254f5e15269a17bc0243 Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 10:19:19 +0100 Subject: [PATCH 30/70] cleanup --- internal/ports/krakenWebSocket.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index f1d968b..c39025f 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -101,10 +101,7 @@ func (socket *krakenWebSocket) Read() (*TickerWithPrice, error) { return nil, err } - msg := string(message) - log.Info(msg) - - err = json.Unmarshal([]byte(msg), &msgAsJson) + err = json.Unmarshal([]byte(message), &msgAsJson) if err != nil { return nil, err } From 1035fc3989192eee75cba54eab1a75a9775e75e1 Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 10:37:06 +0100 Subject: [PATCH 31/70] add mutex in tdexFeeder for isRunning function --- internal/domain/tdexFeeder.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go index 1d539c2..424d501 100644 --- a/internal/domain/tdexFeeder.go +++ b/internal/domain/tdexFeeder.go @@ -1,6 +1,9 @@ package domain -import "errors" +import ( + "errors" + "sync" +) type TdexFeeder interface { @@ -14,6 +17,7 @@ type tdexFeeder struct { targets []Target stopChan chan bool running bool + locker sync.Locker } func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { @@ -22,6 +26,7 @@ func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { targets: targets, stopChan: make(chan bool), running: false, + locker: &sync.Mutex{}, } } @@ -33,7 +38,7 @@ func (t *tdexFeeder) Start() error { t.running = true marketPriceChannel := merge(t.feeds...) - for t.running { + for t.IsRunning() { select { case <-t.stopChan: t.running = false @@ -56,6 +61,8 @@ func (t *tdexFeeder) Stop() { } func (t *tdexFeeder) IsRunning() bool { + t.locker.Lock() + defer t.locker.Unlock() return t.running } From d5c7705a2b6037418ca3f5f79359d83a561237ea Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 10:39:26 +0100 Subject: [PATCH 32/70] delete test file --- scripts/test | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100755 scripts/test diff --git a/scripts/test b/scripts/test deleted file mode 100755 index 3002ae2..0000000 --- a/scripts/test +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -set -e - -PARENT_PATH=$(dirname $( - cd $(dirname $0) - pwd -P -)) - -pushd $PARENT_PATH -go test -v -count=1 -race ./... -popd From be0a97c3396610e7ce88642b6d023a4cd717221b Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 10:39:38 +0100 Subject: [PATCH 33/70] add integration test + shortest in makefile --- Makefile | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 2099678..862c278 100644 --- a/Makefile +++ b/Makefile @@ -52,9 +52,12 @@ vet: @echo "Vet..." @go vet ./... - ## test: runs go unit test with default values -test: fmt - chmod u+x ./scripts/test - ./scripts/test +test: fmt shorttest + +shorttest: + @echo "Testing..." + go test -v -count=1 -race -short ./... +integrationtest: + go test -v -count=1 ./cmd/feederd \ No newline at end of file From 1897e70621d3e6fd81194936d9b09ef23f59f4fd Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 11:12:43 +0100 Subject: [PATCH 34/70] add some info logs --- internal/application/feed_service.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index c539b70..897a2f9 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -69,7 +69,11 @@ func (f *krakenFeedService) Start() { log.Info("Feed service stopped") break; case <-time.After(500 * time.Millisecond): + log.Info("Read socket interval") tickerWithPrice, err := f.krakenWebSocket.Read() + if (tickerWithPrice != nil) { + log.Info("msg =" + string(tickerWithPrice.Ticker)) + } if err != nil { log.Debug("Read message error: ", err) continue From f100974f089dbf14c0c2ef630dca778955896175 Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 11:12:53 +0100 Subject: [PATCH 35/70] add default config to gitignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b11c642..190a006 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ build/ cmd/feederd/tdexd -cmd/feederd/config.json \ No newline at end of file +cmd/feederd/config.json + +config.json \ No newline at end of file From 782e8519f3d6c8d6d2275972cdfbb6588bcd330f Mon Sep 17 00:00:00 2001 From: louisinger Date: Wed, 2 Dec 2020 11:32:36 +0100 Subject: [PATCH 36/70] add diagram --- tdexfeeder.png | Bin 0 -> 35545 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tdexfeeder.png diff --git a/tdexfeeder.png b/tdexfeeder.png new file mode 100644 index 0000000000000000000000000000000000000000..f77934ef4062ed1849419654ca1fe86f7deea145 GIT binary patch literal 35545 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 Rv7&6lBgLl=Pzolm{yzqSu Date: Wed, 2 Dec 2020 11:36:28 +0100 Subject: [PATCH 37/70] Update README.md --- README.md | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 26bec14..edeb8f6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # tdex-feeder -Feeder allows to connect an external price feed to the TDex Daemon to determine the current market price +Feeder allows to connect several price feeds to TDex Daemon(s) in order to automatically update the markets prices. ## Overview @@ -24,11 +24,8 @@ interface exposed from tdex-deamon `UpdateMarketPrice`. # Run with default config and default flags. $ feederd -# Run with debug mode on. -$ feederd -debug - # Run with debug mode and different config path. -$ feederd -debug -conf=./config.json +$ TDEX_FEEDER_CONFIG_PATH=./config.json feederd ``` ## 🖥 Local Development @@ -71,13 +68,6 @@ Builds feeder as static binary and runs the project with default configuration. `make run-linux` -##### Flags - -``` --conf: Configuration File Path. Default: "./config.json" --debug: Log Debug Informations Default: false -``` - ##### Config file Rename the file `./config.example.json` into `./config.json` @@ -86,11 +76,9 @@ connects to kraken socket and to a local instance of tdex-deamon. ``` daemon_endpoint: String with the address and port of gRPC host. Required. -daemon_macaroon: String with the daemon_macaroon necessary for authentication. kraken_ws_endpoint: String with the address and port of kraken socket. Required. markets: Json List with necessary markets informations. Required. -base_asset: String of the Hash of the base asset for gRPC request. Required. -quote_asset: String of the Hash of the quote asset for gRPC request. Required. -kraken_ticker: String with the ticker we want kraken to provide informations on. Required. -interval: Int with the time in secods between gRPC requests. Required. -``` \ No newline at end of file + base_asset: String of the Hash of the base asset for gRPC request. Required. + quote_asset: String of the Hash of the quote asset for gRPC request. Required. + kraken_ticker: String with the ticker we want kraken to provide informations on. Required. +``` From 5285a08f9f4a5a934551be861a32e9bbc94148ec Mon Sep 17 00:00:00 2001 From: Louis Singer <41042567+louisinger@users.noreply.github.com> Date: Wed, 2 Dec 2020 11:38:02 +0100 Subject: [PATCH 38/70] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index edeb8f6..04397ab 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ Feeder allows to connect several price feeds to TDex Daemon(s) in order to autom tdex-feeder connects to exchanges and retrieves market prices in order to consume the gRPC interface exposed from tdex-deamon `UpdateMarketPrice`. +![tdex-schema](./tdexfeeder.png) + ## ⬇️ Run Standalone ### Install From 7ff3e6bd813fb8d8af344a8034fa4dea8d1d2289 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 09:58:33 +0100 Subject: [PATCH 39/70] remove unused uuid --- internal/application/feed_service.go | 7 +-- internal/domain/feed.go | 15 ++---- internal/domain/feeder_test.go | 74 ++++++++++++---------------- 3 files changed, 38 insertions(+), 58 deletions(-) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index 897a2f9..cb1c1c7 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -26,10 +26,7 @@ func NewKrakenFeedService( address string, tickersToMarketMap map[string]domain.Market, ) (FeedService, error) { - newFeed, err := domain.NewFeed() - if err != nil { - return nil, err - } + newFeed := domain.NewFeed() tickersToSubscribe := make([]string, 0) for k := range tickersToMarketMap { @@ -37,7 +34,7 @@ func NewKrakenFeedService( } krakenSocket := ports.NewKrakenWebSocket() - err = krakenSocket.Connect(address, tickersToSubscribe) + err := krakenSocket.Connect(address, tickersToSubscribe) if err != nil { return nil, err } diff --git a/internal/domain/feed.go b/internal/domain/feed.go index c662586..13c8959 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -2,8 +2,6 @@ package domain import ( "sync" - - "github.com/google/uuid" ) type MarketPrice struct { @@ -11,26 +9,21 @@ type MarketPrice struct { Price Price } +// Feed represents a source of MarketPrice data type Feed interface { AddMarketPrice(marketPrice MarketPrice) getMarketPriceChan() <-chan MarketPrice } type feed struct { - id string marketPriceChan chan MarketPrice } -func NewFeed() (Feed, error) { - uuid, err := uuid.NewUUID() - if err != nil { - return nil, err - } - +// NewFeed creates a Feed (i.e an empty channel) +func NewFeed() Feed { return &feed{ - id: uuid.String(), marketPriceChan: make(chan MarketPrice), - }, nil + } } func (f feed) AddMarketPrice(marketPrice MarketPrice) { diff --git a/internal/domain/feeder_test.go b/internal/domain/feeder_test.go index 597d9b5..1759d58 100644 --- a/internal/domain/feeder_test.go +++ b/internal/domain/feeder_test.go @@ -8,60 +8,50 @@ import ( ) func TestFeeder(t *testing.T) { - feed, err := NewFeed() - if err != nil { - t.Error(err) - } - - feedBis, err := NewFeed() - if err != nil { - t.Error(err) - } + feed := NewFeed() + feedBis := NewFeed() target := &mockTarget{ marketPrices: make([]MarketPrice, 0), } - feeder := NewTdexFeeder([]Feed{feed, feedBis}, []Target{target}) - t.Run("should push the feeds data to target", func(t *testing.T) { - marketPrice := MarketPrice{ - Market: Market{ - BaseAsset: "1111", - QuoteAsset: "0000", - }, - Price: Price{ - BasePrice: 0.2, - QuotePrice: 1, - }, - } - - go func () { - err := feeder.Start() - if err != nil { - t.Error(err) - } - }() + marketPrice := MarketPrice{ + Market: Market{ + BaseAsset: "1111", + QuoteAsset: "0000", + }, + Price: Price{ + BasePrice: 0.2, + QuotePrice: 1, + }, + } - time.Sleep(time.Second) - assert.Equal(t, true, feeder.IsRunning()) - - go func () { - for i := 0; i < 5; i++ { - feedBis.AddMarketPrice(marketPrice) - } - }() + go func () { + err := feeder.Start() + if err != nil { + t.Error(err) + } + }() - for i := 0; i < 10; i++ { - feed.AddMarketPrice(marketPrice) + time.Sleep(time.Second) + assert.Equal(t, true, feeder.IsRunning()) + + go func () { + for i := 0; i < 5; i++ { + feedBis.AddMarketPrice(marketPrice) } + }() + + for i := 0; i < 10; i++ { + feed.AddMarketPrice(marketPrice) + } - time.Sleep(500 * time.Millisecond) - feeder.Stop() + time.Sleep(500 * time.Millisecond) + feeder.Stop() - assert.Equal(t, 15, len(target.marketPrices)) - }) + assert.Equal(t, 15, len(target.marketPrices)) } type mockTarget struct { From dd446194a1fee204254f842420b796202647b456 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 10:03:38 +0100 Subject: [PATCH 40/70] remove uuid package --- go.mod | 3 +-- go.sum | 8 ++++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d3d3ea9..2f955af 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,9 @@ module github.com/tdex-network/tdex-feeder go 1.15 require ( - github.com/google/uuid v1.1.2 github.com/gorilla/websocket v1.4.2 github.com/sirupsen/logrus v1.7.0 - github.com/stretchr/testify v1.2.2 + github.com/stretchr/testify v1.6.1 github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77 google.golang.org/grpc v1.33.2 ) diff --git a/go.sum b/go.sum index 06dad2b..c0282ca 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -35,8 +36,11 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77 h1:njOphJsTU5qCJROyzw4ReARDbucTCbfj5t7Mgnnt9u0= github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77/go.mod h1:tVWv01BSMH/neJOsixdDFU5T0ll0OZbPliLXBsYQjA8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -87,5 +91,9 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= From 55f0c3598b96bc30301da80a6edfe8b0e80657b7 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 10:03:49 +0100 Subject: [PATCH 41/70] remove t.Run statements in tests --- internal/application/feed_service_test.go | 38 +++++++++++------------ internal/ports/krakenWebSocket_test.go | 22 ++++++------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go index 9ba8560..0d97669 100644 --- a/internal/application/feed_service_test.go +++ b/internal/application/feed_service_test.go @@ -24,29 +24,27 @@ func TestKrakenFeedService(t *testing.T) { QuoteAsset: quoteAsset, } - t.Run("Start a service should feed the Feed instance", func(t *testing.T) { - svc, err := NewKrakenFeedService(krakenWsEndpoint, tickerMap) + svc, err := NewKrakenFeedService(krakenWsEndpoint, tickerMap) + if err != nil { + t.Error(err) + } + go svc.Start() + defer svc.Stop() + + feed := svc.GetFeed() + target := &mockTarget{marketPrices: []domain.MarketPrice{}} + feeder := domain.NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) + go func() { + err := feeder.Start() if err != nil { t.Error(err) } - go svc.Start() - defer svc.Stop() - - feed := svc.GetFeed() - target := &mockTarget{marketPrices: []domain.MarketPrice{}} - feeder := domain.NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) - go func() { - err := feeder.Start() - if err != nil { - t.Error(err) - } - }() - - time.Sleep(10 * time.Second) - feeder.Stop() - - assert.Equal(t, true, len(target.marketPrices) > 0) - }) + }() + + time.Sleep(10 * time.Second) + feeder.Stop() + + assert.Equal(t, true, len(target.marketPrices) > 0) } type mockTarget struct { diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/krakenWebSocket_test.go index 2f14675..de2525d 100644 --- a/internal/ports/krakenWebSocket_test.go +++ b/internal/ports/krakenWebSocket_test.go @@ -18,21 +18,17 @@ func createAndConnect() (KrakenWebSocket, error) { } func TestConnectToKrakenWebSocket(t *testing.T) { - t.Run("should connect to a valid kraken web socket address", func(t *testing.T) { - _, err := createAndConnect() - assert.Nil(t, err) - }) + _, err := createAndConnect() + assert.Nil(t, err) } func TestRead(t *testing.T) { - t.Run("should read marketInfo from websocket kraken stream", func(t *testing.T) { - ws, err := createAndConnect() - if err != nil { - t.Error(err) - } + ws, err := createAndConnect() + if err != nil { + t.Error(err) + } - tickerWithPrice, err := ws.Read() - assert.Nil(t, err) - assert.NotNil(t, tickerWithPrice) - }) + tickerWithPrice, err := ws.Read() + assert.Nil(t, err) + assert.NotNil(t, tickerWithPrice) } \ No newline at end of file From e3f42001dcbfa3ddfb643435ab17aed19cb5e6dc Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 10:14:29 +0100 Subject: [PATCH 42/70] remove t.Run in main_test.go --- cmd/feederd/main_test.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index 4403d66..be10ff6 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -32,11 +32,9 @@ func TestFeeder(t *testing.T) { runDaemonAndInitConfigFile(t) t.Cleanup(stopAndDeleteContainer) - t.Run("should feed the market using kraken feed", func(t *testing.T) { - go main() - time.Sleep(30 * time.Second) - os.Exit(0) - }) + go main() + time.Sleep(30 * time.Second) + os.Exit(0) } func runDaemonAndInitConfigFile(t *testing.T) { From 0464e7324bea3174156a1a3ce274697159bb486f Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 10:14:41 +0100 Subject: [PATCH 43/70] Add some comments --- internal/domain/feed.go | 2 ++ internal/domain/tdexFeeder.go | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/domain/feed.go b/internal/domain/feed.go index 13c8959..7c35f94 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -26,6 +26,7 @@ func NewFeed() Feed { } } +// AddMarketPrice send a new marketPrice value inside the Feed's channel. func (f feed) AddMarketPrice(marketPrice MarketPrice) { f.marketPriceChan <- marketPrice } @@ -34,6 +35,7 @@ func (f feed) getMarketPriceChan() <-chan MarketPrice { return f.marketPriceChan } +// merge gathers several feeds into a unique channel func merge(feeds ...Feed) <-chan MarketPrice { mergedChan := make(chan MarketPrice) var wg sync.WaitGroup diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go index 424d501..b2d4308 100644 --- a/internal/domain/tdexFeeder.go +++ b/internal/domain/tdexFeeder.go @@ -5,7 +5,6 @@ import ( "sync" ) - type TdexFeeder interface { Start() error Stop() @@ -30,6 +29,8 @@ func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { } } +// Start observe all the feeds chan (using merge function) +// and push the results to all targets func (t *tdexFeeder) Start() error { if t.IsRunning() { return errors.New("the feeder is already started") From e131b871b33591d266add81766ac96fff11baf65 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 17:05:24 +0100 Subject: [PATCH 44/70] add go kraken --- go.mod | 1 + go.sum | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/go.mod b/go.mod index 2f955af..cad5217 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/tdex-network/tdex-feeder go 1.15 require ( + github.com/aopoltorzhicky/go_kraken/websocket v0.0.10 github.com/gorilla/websocket v1.4.2 github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index c0282ca..0ab2f15 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,10 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/aopoltorzhicky/go_kraken v0.0.4 h1:E+G+6Zt3dmvABvp0GOQGhwAJRB4fvvvmr/GwLnnfsAs= +github.com/aopoltorzhicky/go_kraken/rest v0.0.3 h1:oTRp0xqqsq0g83UOu6dCQtbKTk1lZjqtH3TsaDwDt/o= +github.com/aopoltorzhicky/go_kraken/rest v0.0.3/go.mod h1:cen8hPWBicFQ1T4EoseSAkxvCD7zzZYIzxb0OVTHDk0= +github.com/aopoltorzhicky/go_kraken/websocket v0.0.10 h1:3oOS3BIiPXjtuAJ+Yzfi7WjwA6eA7jyu1ESJ3jvxbp8= +github.com/aopoltorzhicky/go_kraken/websocket v0.0.10/go.mod h1:keiaKS3AsahdTS/QD2b220DGzWV/t4tWIjmy+4EruHI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -29,6 +34,7 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= From 48265e794b22a48ec647c677a2d81dca67edd397 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 17:05:38 +0100 Subject: [PATCH 45/70] use go kraken for web socket implementation --- internal/application/feed_service.go | 19 ++-- internal/ports/krakenWebSocket.go | 133 +++++++------------------ internal/ports/krakenWebSocket_test.go | 8 +- 3 files changed, 47 insertions(+), 113 deletions(-) diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index cb1c1c7..ba6ae26 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -1,8 +1,6 @@ package application import ( - "time" - log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" @@ -54,6 +52,10 @@ func (f *krakenFeedService) GetFeed() domain.Feed { func (f *krakenFeedService) Start() { listening := true log.Println("Start listening kraken service") + tickerWithPriceChan, err := f.krakenWebSocket.StartListen() + if err != nil { + log.Fatal(err) + } for listening { select { case <-f.stopChan: @@ -65,20 +67,13 @@ func (f *krakenFeedService) Start() { log.Info("Feed service stopped") break; - case <-time.After(500 * time.Millisecond): - log.Info("Read socket interval") - tickerWithPrice, err := f.krakenWebSocket.Read() - if (tickerWithPrice != nil) { - log.Info("msg =" + string(tickerWithPrice.Ticker)) - } - if err != nil { - log.Debug("Read message error: ", err) - continue - } + case tickerWithPrice := <-tickerWithPriceChan: + log.Debug("Kraken message = " + string(tickerWithPrice.Ticker)) market, ok := f.tickersToMarketMap[tickerWithPrice.Ticker] if !ok { log.Debug("Market not found for ticker: ", tickerWithPrice.Ticker) + continue } f.feed.AddMarketPrice(domain.MarketPrice{ diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index c39025f..c973f36 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -1,142 +1,79 @@ package ports import ( - "encoding/json" "errors" - "fmt" - "net/url" - "strconv" + ws "github.com/aopoltorzhicky/go_kraken/websocket" log "github.com/sirupsen/logrus" - - "github.com/gorilla/websocket" ) type KrakenWebSocket interface { Connect(address string, tickersToSubscribe []string) error - Read() (*TickerWithPrice, error) + StartListen() (chan TickerWithPrice, error) Close() error } type krakenWebSocket struct { - connSocket *websocket.Conn + krakenWS *ws.Client + tickerWithPriceChan chan TickerWithPrice } func NewKrakenWebSocket() KrakenWebSocket { return &krakenWebSocket{ - connSocket: nil, + krakenWS: ws.New(), + tickerWithPriceChan: make(chan TickerWithPrice), } } func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []string) error { - conn, err := connectToSocket(address) + // connect to server + err := socket.krakenWS.Connect() if err != nil { return err } - - var connectMsg map[string]interface{} - - _, msg, err := conn.ReadMessage() + // test if the server is alive + err = socket.krakenWS.Ping() if err != nil { return err } - err = json.Unmarshal(msg, &connectMsg) + // subscribe to tickers + err = socket.krakenWS.SubscribeTicker(tickersToSubscribe) if err != nil { return err } - if status, ok := connectMsg["status"]; !ok || status != "online" { - return errors.New("Error connection with: " + fmt.Sprint(address)) - } - - for _, ticker := range tickersToSubscribe { - var subscribeMsg map[string]interface{} - - msg := createSubscribeToMarketMessage(ticker) - msgBytes, err := json.Marshal(msg) - if err != nil { - return err - } - - err = conn.WriteMessage(websocket.TextMessage, msgBytes) - if err != nil { - return err - } - - _, subscribeMessage, err := conn.ReadMessage() - if err != nil { - return err - } - - err = json.Unmarshal(subscribeMessage, &subscribeMsg) - if err != nil { - return err - } - - if status, ok := subscribeMsg["status"]; !ok || status != "subscribed" { - return errors.New("Error subscribe to: " + fmt.Sprint(ticker)) - } - } - - socket.connSocket = conn - return nil } func (socket *krakenWebSocket) Close() error { - err := socket.connSocket.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) - socket.connSocket = nil - return err + socket.krakenWS.Close() + socket.krakenWS = nil + return nil } -func (socket *krakenWebSocket) Read() (*TickerWithPrice, error) { - if socket.connSocket == nil { +func (socket *krakenWebSocket) StartListen() (chan TickerWithPrice, error) { + if socket.krakenWS == nil { return nil, errors.New("Socket not connected") } - var msgAsJson []interface{} - _, message, err := socket.connSocket.ReadMessage() - if err != nil { - return nil, err - } - - err = json.Unmarshal([]byte(message), &msgAsJson) - if err != nil { - return nil, err - } - - if len(msgAsJson) < 4 { - return nil, errors.New("Invalid message" + fmt.Sprint(message)) - } - - pricesJson := msgAsJson[1].(map[string]interface{}) - priceAsk := pricesJson["c"].([]interface{}) - price, _ := strconv.ParseFloat(priceAsk[0].(string), 64) - - ticker := msgAsJson[3].(string) - - return &TickerWithPrice{ - Ticker: ticker, - Price: price, - }, nil -} - -// ConnectToSocket dials and returns a new client connection to a remote host -func connectToSocket(address string) (*websocket.Conn, error) { - u := url.URL{Scheme: "wss", Host: address, Path: "/"} - - c, _, err := websocket.DefaultDialer.Dial(u.String(), nil) - if err != nil { - return c, err - } - log.Info("Connected to socket:", u.String()) - return c, nil -} + go func() { + for obj := range socket.krakenWS.Listen() { + switch obj := obj.(type) { + case error: + log.Debug("Channel closed: ", obj) + case ws.DataUpdate: + tickerUpdate, ok := obj.Data.(ws.TickerUpdate) + if ok { + result := TickerWithPrice{ + Ticker: tickerUpdate.Pair, + Price: tickerUpdate.Close.Today.(float64), + } + socket.tickerWithPriceChan <- result + } + } + } + }() -// CreateSubscribeToMarketMessage gets a string with a market pair and returns -// a RequestMessage struct with instructions to subscrive to that market pair ticker. -func createSubscribeToMarketMessage(ticker string) requestMessage { - s := subscription{Name: "ticker"} - return requestMessage{"subscribe", []string{ticker}, &s, 0} + return socket.tickerWithPriceChan, nil } \ No newline at end of file diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/krakenWebSocket_test.go index de2525d..0658c88 100644 --- a/internal/ports/krakenWebSocket_test.go +++ b/internal/ports/krakenWebSocket_test.go @@ -22,13 +22,15 @@ func TestConnectToKrakenWebSocket(t *testing.T) { assert.Nil(t, err) } -func TestRead(t *testing.T) { +func TestListen(t *testing.T) { ws, err := createAndConnect() if err != nil { t.Error(err) } - tickerWithPrice, err := ws.Read() + tickerWithPriceChan, err := ws.StartListen() assert.Nil(t, err) - assert.NotNil(t, tickerWithPrice) + + nextTickerWithPrice := <- tickerWithPriceChan + assert.NotNil(t, nextTickerWithPrice) } \ No newline at end of file From 68b78d7b7bdcf09397b64703976be7a4ee119a91 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 17:35:03 +0100 Subject: [PATCH 46/70] change env var name --- README.md | 2 +- cmd/feederd/main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 04397ab..895572e 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ interface exposed from tdex-deamon `UpdateMarketPrice`. $ feederd # Run with debug mode and different config path. -$ TDEX_FEEDER_CONFIG_PATH=./config.json feederd +$ FEEDER_CONFIG_PATH=./config.json feederd ``` ## 🖥 Local Development diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index aad6f67..15c298f 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -15,7 +15,7 @@ import ( ) const ( - envConfigPathKey = "TDEX_FEEDER_CONFIG_PATH" + envConfigPathKey = "FEEDER_CONFIG_PATH" defaultConfigPath = "./config.json" ) From a914ef869950487154e12b86f03340a3f039df60 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 17:35:26 +0100 Subject: [PATCH 47/70] test XBT/USDT ticker --- internal/ports/krakenWebSocket_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/krakenWebSocket_test.go index 0658c88..bc3227f 100644 --- a/internal/ports/krakenWebSocket_test.go +++ b/internal/ports/krakenWebSocket_test.go @@ -8,7 +8,7 @@ import ( const ( address = "ws.kraken.com" - ticker = "LTC/USDT" + ticker = "XBT/USDT" ) func createAndConnect() (KrakenWebSocket, error) { From f6abd1186af4ab37b6680f98d77f909e50f9b586 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 3 Dec 2020 17:37:00 +0100 Subject: [PATCH 48/70] test XBT/USDT in feed_service --- internal/application/feed_service_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go index 0d97669..3ff6021 100644 --- a/internal/application/feed_service_test.go +++ b/internal/application/feed_service_test.go @@ -11,12 +11,10 @@ import ( const ( baseAsset = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225" quoteAsset = "a64b14f3de72bc602d0786e6f034720a879a6b9339d59b09ddd49e1783ed227a" - krakenTicker = "LTC/USDT" + krakenTicker = "XBT/USDT" krakenWsEndpoint = "ws.kraken.com" ) - - func TestKrakenFeedService(t *testing.T) { tickerMap := make(map[string]domain.Market) tickerMap[krakenTicker] = domain.Market{ From 6cec401329cb566e03185900869296bd35da32c6 Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 4 Dec 2020 10:02:41 +0100 Subject: [PATCH 49/70] formatting + cleaning --- internal/adapters/config_service.go | 23 ++++++++++++----------- internal/application/feed_service_test.go | 11 +++++------ internal/domain/feeder_test.go | 3 +-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index 6de3056..f5b41e4 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -13,25 +13,26 @@ 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 { - DaemonEndpoint string `json:"daemon_endpoint"` - KrakenWsEndpoint string `json:"kraken_ws_endpoint"` +type ConfigJson struct { + DaemonEndpoint string `json:"daemon_endpoint"` + KrakenWsEndpoint string `json:"kraken_ws_endpoint"` Markets []MarketJson `json:"markets"` } -type Config struct { - daemonEndpoint string +type Config struct { + daemonEndpoint string krakenWSaddress string - markets map[string]domain.Market + markets map[string]domain.Market } func (config *Config) ToFeederService() application.FeederService { feederSvc := application.NewFeederService(application.NewFeederServiceArgs{ - KrakenWSaddress: config.krakenWSaddress, + KrakenWSaddress: config.krakenWSaddress, OperatorEndpoint: config.daemonEndpoint, - TickerToMarket: config.markets, + TickerToMarket: config.markets, }) return feederSvc @@ -56,7 +57,7 @@ func (config *Config) UnmarshalJSON(data []byte) error { for _, marketJson := range jsonConfig.Markets { configTickerToMarketMap[marketJson.KrakenTicker] = domain.Market{ - BaseAsset: marketJson.BaseAsset, + BaseAsset: marketJson.BaseAsset, QuoteAsset: marketJson.QuoteAsset, } } @@ -89,7 +90,7 @@ func (configJson ConfigJson) validate() error { return err } - err = validateAssetString(marketJson.QuoteAsset) + err = validateAssetString(marketJson.QuoteAsset) if err != nil { return err } @@ -99,7 +100,7 @@ func (configJson ConfigJson) validate() error { } func validateAssetString(asset string) error { - const regularExpression = `[0-9A-Fa-f]{64}` + const regularExpression = `[0-9A-Fa-f]{64}` matched, err := regexp.Match(regularExpression, []byte(asset)) if err != nil { diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go index 3ff6021..ad11cbd 100644 --- a/internal/application/feed_service_test.go +++ b/internal/application/feed_service_test.go @@ -9,16 +9,16 @@ import ( ) const ( - baseAsset = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225" - quoteAsset = "a64b14f3de72bc602d0786e6f034720a879a6b9339d59b09ddd49e1783ed227a" - krakenTicker = "XBT/USDT" + baseAsset = "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225" + quoteAsset = "a64b14f3de72bc602d0786e6f034720a879a6b9339d59b09ddd49e1783ed227a" + krakenTicker = "XBT/USDT" krakenWsEndpoint = "ws.kraken.com" ) func TestKrakenFeedService(t *testing.T) { tickerMap := make(map[string]domain.Market) tickerMap[krakenTicker] = domain.Market{ - BaseAsset: baseAsset, + BaseAsset: baseAsset, QuoteAsset: quoteAsset, } @@ -49,7 +49,6 @@ type mockTarget struct { marketPrices []domain.MarketPrice } -func (t *mockTarget) Push(marketPrice domain.MarketPrice) error { +func (t *mockTarget) Push(marketPrice domain.MarketPrice) { t.marketPrices = append(t.marketPrices, marketPrice) - return nil } diff --git a/internal/domain/feeder_test.go b/internal/domain/feeder_test.go index 1759d58..c813420 100644 --- a/internal/domain/feeder_test.go +++ b/internal/domain/feeder_test.go @@ -58,8 +58,7 @@ type mockTarget struct { marketPrices []MarketPrice } -func (t *mockTarget) Push(marketPrice MarketPrice) error { +func (t *mockTarget) Push(marketPrice MarketPrice) { t.marketPrices = append(t.marketPrices, marketPrice) - return nil } From 942225625ec31f93ff2f41274e4abe60bae5d959 Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 4 Dec 2020 10:03:08 +0100 Subject: [PATCH 50/70] add interval manament to updater_service --- internal/application/updater_service.go | 80 +++++++++++++++++++++---- internal/domain/target.go | 2 +- internal/domain/tdexFeeder.go | 5 +- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go index fd7687a..194f047 100644 --- a/internal/application/updater_service.go +++ b/internal/application/updater_service.go @@ -2,24 +2,84 @@ package application import ( "context" + "sync" + "time" + log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" "github.com/tdex-network/tdex-feeder/internal/ports" ) -// Implements the domain.Target interface +// Implements the domain.Target interface and manage interval for each market type TdexDaemonTarget struct { - Endpoint string - priceUpdater ports.TdexDaemonPriceUpdater + Endpoint string + priceUpdater ports.TdexDaemonPriceUpdater + priceUpdaterLocker *sync.RWMutex + marketsToUpdate map[domain.Market]domain.Price + closeChan chan bool } -func NewTdexDaemonTarget(tdexDaemonOperatorInterfaceEnpoint string) domain.Target { - return &TdexDaemonTarget{ - Endpoint: tdexDaemonOperatorInterfaceEnpoint, - priceUpdater: ports.NewTdexDaemonPriceUpdater(context.Background(), tdexDaemonOperatorInterfaceEnpoint), +// NewTdexDaemonTarget configure a tdexDaemonUpdater using the endpoint +// and start goroutines depending of the configured intervals for each market. +func NewTdexDaemonTarget( + tdexDaemonOperatorInterfaceEnpoint string, + marketToIntervalMap map[domain.Market]time.Duration, +) domain.Target { + now := time.Now() + mapLastSent := make(map[domain.Market]time.Time) + + for market := range marketToIntervalMap { + mapLastSent[market] = now + } + + tdexTarget := &TdexDaemonTarget{ + Endpoint: tdexDaemonOperatorInterfaceEnpoint, + priceUpdater: ports.NewTdexDaemonPriceUpdater(context.Background(), tdexDaemonOperatorInterfaceEnpoint), + priceUpdaterLocker: &sync.RWMutex{}, + closeChan: make(chan bool, 1), + marketsToUpdate: make(map[domain.Market]domain.Price), } + + for market, interval := range marketToIntervalMap { + go func() { + for { + select { + case <-tdexTarget.closeChan: + log.Info("Stop the tdex updater") + break + case <-time.After(interval): + tdexTarget.updatePrice(market) + continue + } + } + }() + } + + return tdexTarget +} + +func (daemon *TdexDaemonTarget) Push(marketPrice domain.MarketPrice) { + daemon.priceUpdaterLocker.Lock() + defer daemon.priceUpdaterLocker.Unlock() + + daemon.marketsToUpdate[marketPrice.Market] = marketPrice.Price +} + +func (daemon *TdexDaemonTarget) Stop() { + daemon.closeChan <- true } -func (daemon *TdexDaemonTarget) Push(marketPrice domain.MarketPrice) error { - return daemon.priceUpdater.UpdateMarketPrice(context.Background(), marketPrice) -} \ No newline at end of file +func (daemon *TdexDaemonTarget) updatePrice(market domain.Market) { + daemon.priceUpdaterLocker.RLock() + defer daemon.priceUpdaterLocker.RUnlock() + + price, ok := daemon.marketsToUpdate[market] + if ok { + err := daemon.priceUpdater.UpdateMarketPrice(context.Background(), domain.MarketPrice{Market: market, Price: price}) + if err != nil { + log.Error("error updatePrice: ", err) + return + } + delete(daemon.marketsToUpdate, market) + } +} diff --git a/internal/domain/target.go b/internal/domain/target.go index 5274ae4..9172d1e 100644 --- a/internal/domain/target.go +++ b/internal/domain/target.go @@ -1,5 +1,5 @@ package domain type Target interface { - Push(marketPrice MarketPrice) error + Push(marketPrice MarketPrice) } \ No newline at end of file diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go index b2d4308..b53240e 100644 --- a/internal/domain/tdexFeeder.go +++ b/internal/domain/tdexFeeder.go @@ -46,10 +46,7 @@ func (t *tdexFeeder) Start() error { break; case marketPrice := <-marketPriceChannel: for _, target := range t.targets { - err := target.Push(marketPrice) - if err != nil { - panic(err) - } + target.Push(marketPrice) } } } From 123a32aef7ffcf28cbc9eb5aaaeef93012ae483b Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 4 Dec 2020 10:10:13 +0100 Subject: [PATCH 51/70] add interval config in config_service --- internal/adapters/config_service.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index f5b41e4..c4b4d30 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "regexp" + "time" "github.com/tdex-network/tdex-feeder/internal/application" "github.com/tdex-network/tdex-feeder/internal/domain" @@ -26,6 +27,7 @@ type Config struct { daemonEndpoint string krakenWSaddress string markets map[string]domain.Market + marketIntervals map[domain.Market]time.Duration } func (config *Config) ToFeederService() application.FeederService { @@ -54,15 +56,20 @@ func (config *Config) UnmarshalJSON(data []byte) error { config.krakenWSaddress = jsonConfig.KrakenWsEndpoint configTickerToMarketMap := make(map[string]domain.Market) + marketIntervalsMap := make(map[domain.Market]time.Duration) for _, marketJson := range jsonConfig.Markets { - configTickerToMarketMap[marketJson.KrakenTicker] = domain.Market{ + market := domain.Market{ BaseAsset: marketJson.BaseAsset, QuoteAsset: marketJson.QuoteAsset, } + + configTickerToMarketMap[marketJson.KrakenTicker] = market + marketIntervalsMap[market] = time.Duration(marketJson.Interval) * time.Millisecond } config.markets = configTickerToMarketMap + config.marketIntervals = marketIntervalsMap return nil } @@ -94,6 +101,10 @@ func (configJson ConfigJson) validate() error { if err != nil { return err } + + if marketJson.Interval < 0 { + return errors.New("interval must be greater (or equal) than 0") + } } return nil From 61a9b2350dcfd212cdc822a7d2ca7d55c14237fe Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 4 Dec 2020 10:28:35 +0100 Subject: [PATCH 52/70] create feeder from config --- internal/adapters/config_service.go | 1 + internal/application/feeder_service.go | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index c4b4d30..2c73830 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -35,6 +35,7 @@ func (config *Config) ToFeederService() application.FeederService { KrakenWSaddress: config.krakenWSaddress, OperatorEndpoint: config.daemonEndpoint, TickerToMarket: config.markets, + MarketToInterval: config.marketIntervals, }) return feederSvc diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go index bc51200..cce57b0 100644 --- a/internal/application/feeder_service.go +++ b/internal/application/feeder_service.go @@ -1,6 +1,8 @@ package application import ( + "time" + log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" ) @@ -11,18 +13,20 @@ type FeederService interface { } type feederService struct { - tdexFeeder domain.TdexFeeder + tdexFeeder domain.TdexFeeder krakenService FeedService + target *TdexDaemonTarget } type NewFeederServiceArgs struct { OperatorEndpoint string - KrakenWSaddress string - TickerToMarket map[string]domain.Market + MarketToInterval map[domain.Market]time.Duration + KrakenWSaddress string + TickerToMarket map[string]domain.Market } func NewFeederService(args NewFeederServiceArgs) FeederService { - target := NewTdexDaemonTarget(args.OperatorEndpoint) + target := NewTdexDaemonTarget(args.OperatorEndpoint, args.MarketToInterval) krakenFeedService, err := NewKrakenFeedService(args.KrakenWSaddress, args.TickerToMarket) if err != nil { log.Fatal(err) @@ -31,8 +35,9 @@ func NewFeederService(args NewFeederServiceArgs) FeederService { feeder := domain.NewTdexFeeder([]domain.Feed{krakenFeedService.GetFeed()}, []domain.Target{target}) return &feederService{ - tdexFeeder: feeder, + tdexFeeder: feeder, krakenService: krakenFeedService, + target: target.(*TdexDaemonTarget), } } @@ -44,6 +49,7 @@ func (feeder *feederService) Start() error { func (feeder *feederService) Stop() error { feeder.krakenService.Stop() + feeder.target.Stop() feeder.tdexFeeder.Stop() - return nil -} \ No newline at end of file + return nil +} From caafd768a43f9ec6d39a6aa814fd2452c9b48658 Mon Sep 17 00:00:00 2001 From: louisinger Date: Fri, 4 Dec 2020 15:06:45 +0100 Subject: [PATCH 53/70] formatting + cleaning --- cmd/feederd/main.go | 6 ++---- cmd/feederd/main_test.go | 18 +++++++++--------- internal/application/feed_service.go | 24 ++++++++++++------------ internal/application/updater_service.go | 6 +++--- internal/domain/feed.go | 12 ++++++------ internal/domain/feeder_test.go | 23 +++++++++++------------ internal/domain/market.go | 4 ++-- internal/domain/price.go | 4 ++-- internal/domain/target.go | 4 ++-- internal/domain/tdexFeeder.go | 21 +++++++++------------ internal/ports/krakenWebSocket.go | 8 ++++---- internal/ports/krakenWebSocket_test.go | 6 +++--- internal/ports/tdexDaemonPriceUpdater.go | 16 ++++++++-------- internal/ports/types.go | 19 +------------------ 14 files changed, 74 insertions(+), 97 deletions(-) diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index 15c298f..e0cf60c 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -15,7 +15,7 @@ import ( ) const ( - envConfigPathKey = "FEEDER_CONFIG_PATH" + envConfigPathKey = "FEEDER_CONFIG_PATH" defaultConfigPath = "./config.json" ) @@ -31,9 +31,8 @@ func main() { } feeder := configFileToFeederService(envConfigPath) - log.Info("Start the feeder") - go func () { + go func() { err := feeder.Start() if err != nil { log.Fatal(err) @@ -72,4 +71,3 @@ func configFileToFeederService(configFilePath string) application.FeederService feeder := config.ToFeederService() return feeder } - diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index be10ff6..40e7cad 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -16,12 +16,12 @@ import ( ) const ( - containerName = "tdexFeederContainerTest" - daemonEndpoint = "127.0.0.1:9000" + containerName = "tdexFeederContainerTest" + daemonEndpoint = "127.0.0.1:9000" krakenWsEndpoint = "ws.kraken.com" // nigiriUrl = "https://nigiri.network/liquid/api" nigiriUrl = "http://localhost:3001" - password = "vulpemsecret" + password = "vulpemsecret" ) func TestFeeder(t *testing.T) { @@ -41,13 +41,14 @@ func runDaemonAndInitConfigFile(t *testing.T) { usdt := runDaemonAndCreateMarket(t) configJson := adapters.ConfigJson{ - DaemonEndpoint: daemonEndpoint, + DaemonEndpoint: daemonEndpoint, KrakenWsEndpoint: krakenWsEndpoint, Markets: []adapters.MarketJson{ adapters.MarketJson{ KrakenTicker: "LTC/USDT", - BaseAsset: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", - QuoteAsset: usdt, + BaseAsset: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", + QuoteAsset: usdt, + Interval: 500, }, }, } @@ -70,7 +71,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", @@ -119,7 +120,7 @@ func runDaemonAndCreateMarket(t *testing.T) string { address := depositMarketResult["address"].(string) usdt := fundMarketAddress(t, address) - + return usdt } @@ -201,7 +202,6 @@ func faucet(address string) (string, error) { return respBody["txId"], nil } - func execute(command string, args ...string) (string, error) { cmd := exec.Command(command, args...) var out bytes.Buffer diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index ba6ae26..d1087d8 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -8,20 +8,20 @@ import ( ) type FeedService interface { - Start() + Start() Stop() GetFeed() domain.Feed } type krakenFeedService struct { - feed domain.Feed - krakenWebSocket ports.KrakenWebSocket - stopChan chan bool + feed domain.Feed + krakenWebSocket ports.KrakenWebSocket + stopChan chan bool tickersToMarketMap map[string]domain.Market } func NewKrakenFeedService( - address string, + address string, tickersToMarketMap map[string]domain.Market, ) (FeedService, error) { newFeed := domain.NewFeed() @@ -36,11 +36,11 @@ func NewKrakenFeedService( if err != nil { return nil, err } - + return &krakenFeedService{ - krakenWebSocket: krakenSocket, - feed: newFeed, - stopChan: make(chan bool), + krakenWebSocket: krakenSocket, + feed: newFeed, + stopChan: make(chan bool), tickersToMarketMap: tickersToMarketMap, }, nil } @@ -66,7 +66,7 @@ func (f *krakenFeedService) Start() { } log.Info("Feed service stopped") - break; + break case tickerWithPrice := <-tickerWithPriceChan: log.Debug("Kraken message = " + string(tickerWithPrice.Ticker)) @@ -79,7 +79,7 @@ func (f *krakenFeedService) Start() { f.feed.AddMarketPrice(domain.MarketPrice{ Market: market, Price: domain.Price{ - BasePrice: 1 / float32(tickerWithPrice.Price), + BasePrice: 1 / float32(tickerWithPrice.Price), QuotePrice: float32(tickerWithPrice.Price), }, }) @@ -89,4 +89,4 @@ func (f *krakenFeedService) Start() { func (f *krakenFeedService) Stop() { f.stopChan <- true -} \ No newline at end of file +} diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go index 194f047..5642eaf 100644 --- a/internal/application/updater_service.go +++ b/internal/application/updater_service.go @@ -41,18 +41,18 @@ func NewTdexDaemonTarget( } for market, interval := range marketToIntervalMap { - go func() { + go func(duration time.Duration, market domain.Market) { for { select { case <-tdexTarget.closeChan: log.Info("Stop the tdex updater") break - case <-time.After(interval): + case <-time.After(duration): tdexTarget.updatePrice(market) continue } } - }() + }(interval, market) } return tdexTarget diff --git a/internal/domain/feed.go b/internal/domain/feed.go index 7c35f94..0a70601 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -6,13 +6,13 @@ import ( type MarketPrice struct { Market Market - Price Price + Price Price } // Feed represents a source of MarketPrice data type Feed interface { AddMarketPrice(marketPrice MarketPrice) - getMarketPriceChan() <-chan MarketPrice + getMarketPriceChan() <-chan MarketPrice } type feed struct { @@ -45,16 +45,16 @@ func merge(feeds ...Feed) <-chan MarketPrice { c := feed.getMarketPriceChan() go func(c <-chan MarketPrice) { for marketPrice := range c { - mergedChan <- marketPrice - } + mergedChan <- marketPrice + } wg.Done() }(c) } - + go func() { wg.Wait() close(mergedChan) }() return mergedChan -} \ No newline at end of file +} diff --git a/internal/domain/feeder_test.go b/internal/domain/feeder_test.go index c813420..0a3ebf6 100644 --- a/internal/domain/feeder_test.go +++ b/internal/domain/feeder_test.go @@ -8,27 +8,27 @@ import ( ) func TestFeeder(t *testing.T) { - feed := NewFeed() - feedBis := NewFeed() + feed := NewFeed() + feedBis := NewFeed() target := &mockTarget{ marketPrices: make([]MarketPrice, 0), } feeder := NewTdexFeeder([]Feed{feed, feedBis}, []Target{target}) - + marketPrice := MarketPrice{ Market: Market{ - BaseAsset: "1111", + BaseAsset: "1111", QuoteAsset: "0000", }, - Price: Price{ - BasePrice: 0.2, + Price: Price{ + BasePrice: 0.2, QuotePrice: 1, }, } - go func () { + go func() { err := feeder.Start() if err != nil { t.Error(err) @@ -37,16 +37,16 @@ func TestFeeder(t *testing.T) { time.Sleep(time.Second) assert.Equal(t, true, feeder.IsRunning()) - - go func () { + + go func() { for i := 0; i < 5; i++ { feedBis.AddMarketPrice(marketPrice) - } + } }() for i := 0; i < 10; i++ { feed.AddMarketPrice(marketPrice) - } + } time.Sleep(500 * time.Millisecond) feeder.Stop() @@ -61,4 +61,3 @@ type mockTarget struct { func (t *mockTarget) Push(marketPrice MarketPrice) { t.marketPrices = append(t.marketPrices, marketPrice) } - diff --git a/internal/domain/market.go b/internal/domain/market.go index 3eb8a1b..c954a50 100644 --- a/internal/domain/market.go +++ b/internal/domain/market.go @@ -1,6 +1,6 @@ package domain type Market struct { - BaseAsset string + BaseAsset string QuoteAsset string -} \ No newline at end of file +} diff --git a/internal/domain/price.go b/internal/domain/price.go index 534fb44..c017327 100644 --- a/internal/domain/price.go +++ b/internal/domain/price.go @@ -1,6 +1,6 @@ package domain type Price struct { - BasePrice float32 + BasePrice float32 QuotePrice float32 -} \ No newline at end of file +} diff --git a/internal/domain/target.go b/internal/domain/target.go index 9172d1e..a93beeb 100644 --- a/internal/domain/target.go +++ b/internal/domain/target.go @@ -1,5 +1,5 @@ package domain type Target interface { - Push(marketPrice MarketPrice) -} \ No newline at end of file + Push(marketPrice MarketPrice) +} diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdexFeeder.go index b53240e..1add52b 100644 --- a/internal/domain/tdexFeeder.go +++ b/internal/domain/tdexFeeder.go @@ -12,20 +12,20 @@ type TdexFeeder interface { } type tdexFeeder struct { - feeds []Feed - targets []Target + feeds []Feed + targets []Target stopChan chan bool - running bool - locker sync.Locker + running bool + locker sync.Locker } func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { return &tdexFeeder{ - feeds: feeds, - targets: targets, + feeds: feeds, + targets: targets, stopChan: make(chan bool), - running: false, - locker: &sync.Mutex{}, + running: false, + locker: &sync.Mutex{}, } } @@ -43,7 +43,7 @@ func (t *tdexFeeder) Start() error { select { case <-t.stopChan: t.running = false - break; + break case marketPrice := <-marketPriceChannel: for _, target := range t.targets { target.Push(marketPrice) @@ -63,6 +63,3 @@ func (t *tdexFeeder) IsRunning() bool { defer t.locker.Unlock() return t.running } - - - diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index c973f36..ec29c60 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -14,13 +14,13 @@ type KrakenWebSocket interface { } type krakenWebSocket struct { - krakenWS *ws.Client + krakenWS *ws.Client tickerWithPriceChan chan TickerWithPrice } func NewKrakenWebSocket() KrakenWebSocket { return &krakenWebSocket{ - krakenWS: ws.New(), + krakenWS: ws.New(), tickerWithPriceChan: make(chan TickerWithPrice), } } @@ -67,7 +67,7 @@ func (socket *krakenWebSocket) StartListen() (chan TickerWithPrice, error) { if ok { result := TickerWithPrice{ Ticker: tickerUpdate.Pair, - Price: tickerUpdate.Close.Today.(float64), + Price: tickerUpdate.Close.Today.(float64), } socket.tickerWithPriceChan <- result } @@ -76,4 +76,4 @@ func (socket *krakenWebSocket) StartListen() (chan TickerWithPrice, error) { }() return socket.tickerWithPriceChan, nil -} \ No newline at end of file +} diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/krakenWebSocket_test.go index bc3227f..a780b4c 100644 --- a/internal/ports/krakenWebSocket_test.go +++ b/internal/ports/krakenWebSocket_test.go @@ -8,7 +8,7 @@ import ( const ( address = "ws.kraken.com" - ticker = "XBT/USDT" + ticker = "XBT/USDT" ) func createAndConnect() (KrakenWebSocket, error) { @@ -31,6 +31,6 @@ func TestListen(t *testing.T) { tickerWithPriceChan, err := ws.StartListen() assert.Nil(t, err) - nextTickerWithPrice := <- tickerWithPriceChan + nextTickerWithPrice := <-tickerWithPriceChan assert.NotNil(t, nextTickerWithPrice) -} \ No newline at end of file +} diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdexDaemonPriceUpdater.go index 3309b8c..b77a949 100644 --- a/internal/ports/tdexDaemonPriceUpdater.go +++ b/internal/ports/tdexDaemonPriceUpdater.go @@ -44,14 +44,14 @@ func (updater *tdexDaemonPriceUpdater) UpdateMarketPrice(ctx context.Context, ma } args := pboperator.UpdateMarketPriceRequest{ - Market: &types.Market{ - BaseAsset: marketPrice.Market.BaseAsset, + Market: &types.Market{ + BaseAsset: marketPrice.Market.BaseAsset, QuoteAsset: marketPrice.Market.QuoteAsset, - }, - Price: &types.Price{ - BasePrice: marketPrice.Price.BasePrice, - QuotePrice: marketPrice.Price.BasePrice, - }, + }, + Price: &types.Price{ + BasePrice: marketPrice.Price.BasePrice, + QuotePrice: marketPrice.Price.BasePrice, + }, } _, err := updater.clientGRPC.UpdateMarketPrice(ctx, &args) @@ -70,4 +70,4 @@ func connectToGRPC(ctx context.Context, daemonEndpoint string) (*grpc.ClientConn } log.Println("Connected to gRPC:", daemonEndpoint) return conn, nil -} \ No newline at end of file +} diff --git a/internal/ports/types.go b/internal/ports/types.go index 8359d04..fc4119d 100644 --- a/internal/ports/types.go +++ b/internal/ports/types.go @@ -2,22 +2,5 @@ package ports type TickerWithPrice struct { Ticker string - Price float64 + Price float64 } - -type subscription struct { - Name string `json:"name"` - Interval int `json:"interval,omitempty"` - Token string `json:"token,omitempty"` - Depth int `json:"depth,omitempty"` - Snapshop bool `json:"snapshot,omitempty"` -} - -// RequestMessage is the data structure used to create -// jsons in order subscribe to market updates on Kraken -type requestMessage struct { - Event string `json:"event"` - Pair []string `json:"pair,omitempty"` - Subscription *subscription `json:"subscription,omitempty"` - Reqid int `json:"reqid,omitempty"` -} \ No newline at end of file From 956eaeafaa1f7cc37d4953e9646ccdc59d4a6234 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 11:47:35 +0100 Subject: [PATCH 54/70] better error handling in config_service.go --- internal/adapters/config_service.go | 13 ++++++------- internal/adapters/errors.go | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 internal/adapters/errors.go diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index 2c73830..91dba67 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -2,7 +2,6 @@ package adapters import ( "encoding/json" - "errors" "regexp" "time" @@ -77,20 +76,20 @@ func (config *Config) UnmarshalJSON(data []byte) error { func (configJson ConfigJson) validate() error { if configJson.DaemonEndpoint == "" { - return errors.New("daemon endpoint is empty") + return ErrDaemonEndpointIsEmpty } if configJson.KrakenWsEndpoint == "" { - return errors.New("kraken websocket endpoint is empty") + return ErrKrakenEndpointIsEmpty } if len(configJson.Markets) == 0 { - return errors.New("need at least 1 market to feed") + return ErrNeedAtLeastOneMarketToFeed } for _, marketJson := range configJson.Markets { if marketJson.KrakenTicker == "" { - return errors.New("krakenTicker is empty") + return ErrKrakenTickerIsEmpty } err := validateAssetString(marketJson.BaseAsset) @@ -104,7 +103,7 @@ func (configJson ConfigJson) validate() error { } if marketJson.Interval < 0 { - return errors.New("interval must be greater (or equal) than 0") + return ErrIntervalIsNotPositiveNumber } } @@ -120,7 +119,7 @@ func validateAssetString(asset string) error { } if !matched { - return errors.New(asset + " is an invalid asset string.") + return ErrInvalidAssetHash{asset: asset}.Error() } return nil diff --git a/internal/adapters/errors.go b/internal/adapters/errors.go new file mode 100644 index 0000000..8028500 --- /dev/null +++ b/internal/adapters/errors.go @@ -0,0 +1,17 @@ +package adapters + +import "errors" + +var ErrDaemonEndpointIsEmpty = errors.New("daemon endpoint is empty") +var ErrKrakenEndpointIsEmpty = errors.New("kraken websocket endpoint is empty") +var ErrNeedAtLeastOneMarketToFeed = errors.New("need at least 1 market to feed") +var ErrKrakenTickerIsEmpty = errors.New("krakenTicker should not be an empty string") +var ErrIntervalIsNotPositiveNumber = errors.New("interval must be greater (or equal) than 0") + +type ErrInvalidAssetHash struct { + asset string +} + +func (e ErrInvalidAssetHash) Error() error { + return errors.New("the string '" + e.asset + "' is an invalid asset string.") +} From 5793aaf2b2a2cac163235e103ee9b3d09a643f88 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 11:48:46 +0100 Subject: [PATCH 55/70] ErrInvalidAssetHash implements error interface --- internal/adapters/config_service.go | 2 +- internal/adapters/errors.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go index 91dba67..c78201a 100644 --- a/internal/adapters/config_service.go +++ b/internal/adapters/config_service.go @@ -119,7 +119,7 @@ func validateAssetString(asset string) error { } if !matched { - return ErrInvalidAssetHash{asset: asset}.Error() + return ErrInvalidAssetHash{asset: asset} } return nil diff --git a/internal/adapters/errors.go b/internal/adapters/errors.go index 8028500..e753cf5 100644 --- a/internal/adapters/errors.go +++ b/internal/adapters/errors.go @@ -12,6 +12,6 @@ type ErrInvalidAssetHash struct { asset string } -func (e ErrInvalidAssetHash) Error() error { - return errors.New("the string '" + e.asset + "' is an invalid asset string.") +func (e ErrInvalidAssetHash) Error() string { + return "the string '" + e.asset + "' is an invalid asset string." } From e6eeca3918eb61e9c4808f1a04d259a621b71f17 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 12:29:52 +0100 Subject: [PATCH 56/70] add go docs + clean some code --- internal/adapters/errors.go | 12 +++++++----- internal/application/feed_service.go | 12 ++++++++---- internal/application/feeder_service.go | 6 +++++- internal/application/updater_service.go | 3 +++ internal/domain/feed.go | 8 ++++---- internal/ports/krakenWebSocket.go | 13 +++++++------ internal/ports/tdexDaemonPriceUpdater.go | 2 +- 7 files changed, 35 insertions(+), 21 deletions(-) diff --git a/internal/adapters/errors.go b/internal/adapters/errors.go index e753cf5..ef9bfa6 100644 --- a/internal/adapters/errors.go +++ b/internal/adapters/errors.go @@ -2,11 +2,13 @@ package adapters import "errors" -var ErrDaemonEndpointIsEmpty = errors.New("daemon endpoint is empty") -var ErrKrakenEndpointIsEmpty = errors.New("kraken websocket endpoint is empty") -var ErrNeedAtLeastOneMarketToFeed = errors.New("need at least 1 market to feed") -var ErrKrakenTickerIsEmpty = errors.New("krakenTicker should not be an empty string") -var ErrIntervalIsNotPositiveNumber = errors.New("interval must be greater (or equal) than 0") +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") + ErrIntervalIsNotPositiveNumber = errors.New("interval must be greater (or equal) than 0") +) type ErrInvalidAssetHash struct { asset string diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index d1087d8..d39e4aa 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -45,10 +45,8 @@ func NewKrakenFeedService( }, nil } -func (f *krakenFeedService) GetFeed() domain.Feed { - return f.feed -} - +// Start is the main function of krakenFeedService +// when start, the services is listening for new data from kraken server func (f *krakenFeedService) Start() { listening := true log.Println("Start listening kraken service") @@ -87,6 +85,12 @@ func (f *krakenFeedService) Start() { } } +// Stop just send data to the stopChan in order to stop listening from kraken web socket func (f *krakenFeedService) Stop() { f.stopChan <- true } + +// GetFeed is a getter function for kraken's feed member +func (f *krakenFeedService) GetFeed() domain.Feed { + return f.feed +} diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go index cce57b0..84651a5 100644 --- a/internal/application/feeder_service.go +++ b/internal/application/feeder_service.go @@ -27,12 +27,16 @@ type NewFeederServiceArgs struct { func NewFeederService(args NewFeederServiceArgs) FeederService { target := NewTdexDaemonTarget(args.OperatorEndpoint, args.MarketToInterval) + krakenFeedService, err := NewKrakenFeedService(args.KrakenWSaddress, args.TickerToMarket) if err != nil { log.Fatal(err) } - feeder := domain.NewTdexFeeder([]domain.Feed{krakenFeedService.GetFeed()}, []domain.Target{target}) + feeder := domain.NewTdexFeeder( + []domain.Feed{krakenFeedService.GetFeed()}, + []domain.Target{target}, + ) return &feederService{ tdexFeeder: feeder, diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go index 5642eaf..3cc408b 100644 --- a/internal/application/updater_service.go +++ b/internal/application/updater_service.go @@ -58,6 +58,8 @@ func NewTdexDaemonTarget( return tdexTarget } +// Push is a method of the Target interface +// The tdexDaemonTarget stores the marketPrice in a local cache. func (daemon *TdexDaemonTarget) Push(marketPrice domain.MarketPrice) { daemon.priceUpdaterLocker.Lock() defer daemon.priceUpdaterLocker.Unlock() @@ -65,6 +67,7 @@ func (daemon *TdexDaemonTarget) Push(marketPrice domain.MarketPrice) { daemon.marketsToUpdate[marketPrice.Market] = marketPrice.Price } +// Stop is used to stop all the goroutines launched in NewTdexDaemonTarget func (daemon *TdexDaemonTarget) Stop() { daemon.closeChan <- true } diff --git a/internal/domain/feed.go b/internal/domain/feed.go index 0a70601..0756b6b 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -31,10 +31,6 @@ func (f feed) AddMarketPrice(marketPrice MarketPrice) { f.marketPriceChan <- marketPrice } -func (f feed) getMarketPriceChan() <-chan MarketPrice { - return f.marketPriceChan -} - // merge gathers several feeds into a unique channel func merge(feeds ...Feed) <-chan MarketPrice { mergedChan := make(chan MarketPrice) @@ -58,3 +54,7 @@ func merge(feeds ...Feed) <-chan MarketPrice { return mergedChan } + +func (f feed) getMarketPriceChan() <-chan MarketPrice { + return f.marketPriceChan +} diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/krakenWebSocket.go index ec29c60..1e377a1 100644 --- a/internal/ports/krakenWebSocket.go +++ b/internal/ports/krakenWebSocket.go @@ -25,6 +25,7 @@ func NewKrakenWebSocket() KrakenWebSocket { } } +// Connect method will connect to the websocket kraken server, ping it and subscribe to tickers threads. func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []string) error { // connect to server err := socket.krakenWS.Connect() @@ -46,12 +47,6 @@ func (socket *krakenWebSocket) Connect(address string, tickersToSubscribe []stri return nil } -func (socket *krakenWebSocket) Close() error { - socket.krakenWS.Close() - socket.krakenWS = nil - return nil -} - func (socket *krakenWebSocket) StartListen() (chan TickerWithPrice, error) { if socket.krakenWS == nil { return nil, errors.New("Socket not connected") @@ -77,3 +72,9 @@ func (socket *krakenWebSocket) StartListen() (chan TickerWithPrice, error) { return socket.tickerWithPriceChan, nil } + +func (socket *krakenWebSocket) Close() error { + socket.krakenWS.Close() + socket.krakenWS = nil + return nil +} diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdexDaemonPriceUpdater.go index b77a949..594d5d8 100644 --- a/internal/ports/tdexDaemonPriceUpdater.go +++ b/internal/ports/tdexDaemonPriceUpdater.go @@ -33,8 +33,8 @@ type tdexDaemonPriceUpdater struct { clientGRPC pboperator.OperatorClient } +// UpdateMarketPrice gets a marketPrice and sends updateMarketPrice request through gRPC client. func (updater *tdexDaemonPriceUpdater) UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error { - if marketPrice.Price.BasePrice == 0.00 { return errors.New("Base price is 0.00") } From 3c03cc552413ec4446a6b45bec71e161dd033857 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 12:32:51 +0100 Subject: [PATCH 57/70] remove gorilla/websocket package --- go.mod | 1 - go.sum | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index cad5217..93dd10e 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/gorilla/websocket v1.4.2 github.com/sirupsen/logrus v1.7.0 github.com/stretchr/testify v1.6.1 github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77 diff --git a/go.sum b/go.sum index 0ab2f15..dd0f2db 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,7 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= From 70a254116a400f5ab91cabcf77b4500b89e8c811 Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 12:35:21 +0100 Subject: [PATCH 58/70] new config.example file --- config.example.json | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) mode change 100644 => 100755 config.example.json diff --git a/config.example.json b/config.example.json old mode 100644 new mode 100755 index fd6bdac..15c7ec5 --- a/config.example.json +++ b/config.example.json @@ -1,13 +1,10 @@ { - "daemon_endpoint": "localhost:9000", - "daemon_macaroon": "string", - "kraken_ws_endpoint": "ws.kraken.com", - "markets": [ - { - "base_asset": "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", - "quote_asset": "691e6b9e00cb75f6383f2581ef001f2695074746e213d21771dff5a890f21104", - "kraken_ticker": "XBT/USD", - "interval": 10 - } - ] + "daemon_endpoint":"127.0.0.1:9000", + "kraken_ws_endpoint":"ws.kraken.com", + "markets":[{ + "base_asset":"5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", + "quote_asset":"e3ec88b0ed9228c3337f746f5d9ea20c1e68bbe1f73d94621c1e8452bebb9e22", + "kraken_ticker":"LTC/USDT", + "interval":500 + }] } \ No newline at end of file From 7f330f852299d40c815f1e6d6591c40aaaae5c8b Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 12:52:22 +0100 Subject: [PATCH 59/70] update README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 895572e..10562f2 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,5 @@ markets: Json List with necessary markets informations. Required. base_asset: String of the Hash of the base asset for gRPC request. Required. quote_asset: String of the Hash of the quote asset for gRPC request. Required. kraken_ticker: String with the ticker we want kraken to provide informations on. Required. + interval: the minimum time in milliseconds between two updateMarketPrice requests. Required. ``` From a91c46c811c8f19c38b54aa59a4e285ae2d4113d Mon Sep 17 00:00:00 2001 From: louisinger Date: Mon, 7 Dec 2020 19:11:51 +0100 Subject: [PATCH 60/70] Update dockerfile --- Dockerfile | 4 ++-- README.md | 8 +++++--- internal/ports/tdexDaemonPriceUpdater.go | 1 + 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 71f6082..6c0c381 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN go mod download COPY . . -RUN go build -o feederd-linux cmd/feederd/main.go +RUN GOOS=linux GOARCH=amd64 go build -o feederd-linux cmd/feederd/main.go WORKDIR /build @@ -20,4 +20,4 @@ RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates COPY --from=builder /build/ / -CMD ["/feederd-linux","-debug","-conf=./data/config.json"] \ No newline at end of file +CMD ["/feederd-linux"] \ No newline at end of file diff --git a/README.md b/README.md index 10562f2..7349f8d 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ Build and use `feederd` with docker. At the root of the repository ``` -docker build -t tdex-feederd . +docker build --pull --rm -f 'Dockerfile' -t feederd:latest . ``` #### Run the daemon @@ -50,9 +50,11 @@ docker build -t tdex-feederd . Create a [config.json](#config-file) file and run the following command in the same folder: ``` -docker run -it -d --net=host -v $PWD/config.json:/data/config.json tdex-feederd +docker run -it --name feederd -v $HOME/config.json:/config.json --network="host" feederd ``` -`--net=host` in case you're running tdex-deamon locally +the `$HOME/config.json` is the path to the feederd configuration file. + +> `--net=host` in case you're running tdex-deamon locally ### Build it yourself diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdexDaemonPriceUpdater.go index 594d5d8..c309d20 100644 --- a/internal/ports/tdexDaemonPriceUpdater.go +++ b/internal/ports/tdexDaemonPriceUpdater.go @@ -16,6 +16,7 @@ type TdexDaemonPriceUpdater interface { UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error } +// NewTdexDaemonPriceUpdater uses the operatorInterfaceEndpoint to create a gRPC client. func NewTdexDaemonPriceUpdater(ctx context.Context, operatorInterfaceEndpoint string) TdexDaemonPriceUpdater { connGrpc, err := connectToGRPC(ctx, operatorInterfaceEndpoint) if err != nil { From fbbc6eaad58bc559afc284c26552eb5f41f2046d Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 10:31:04 +0100 Subject: [PATCH 61/70] replace baseprice by quoteprice in priceupdater --- internal/ports/tdexDaemonPriceUpdater.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdexDaemonPriceUpdater.go index c309d20..87bbb45 100644 --- a/internal/ports/tdexDaemonPriceUpdater.go +++ b/internal/ports/tdexDaemonPriceUpdater.go @@ -51,7 +51,7 @@ func (updater *tdexDaemonPriceUpdater) UpdateMarketPrice(ctx context.Context, ma }, Price: &types.Price{ BasePrice: marketPrice.Price.BasePrice, - QuotePrice: marketPrice.Price.BasePrice, + QuotePrice: marketPrice.Price.QuotePrice, }, } From 2a66c3edb0d288fa46e18d7d1a3e932ec91a0fc4 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 10:55:26 +0100 Subject: [PATCH 62/70] camel case to underscore filenames --- internal/domain/{tdexFeeder.go => tdex_feeder.go} | 0 internal/ports/{krakenWebSocket.go => kraken_websocket.go} | 0 .../ports/{krakenWebSocket_test.go => kraken_websocket_test.go} | 0 .../{tdexDaemonPriceUpdater.go => tdex_daemon_price_updater.go} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename internal/domain/{tdexFeeder.go => tdex_feeder.go} (100%) rename internal/ports/{krakenWebSocket.go => kraken_websocket.go} (100%) rename internal/ports/{krakenWebSocket_test.go => kraken_websocket_test.go} (100%) rename internal/ports/{tdexDaemonPriceUpdater.go => tdex_daemon_price_updater.go} (100%) diff --git a/internal/domain/tdexFeeder.go b/internal/domain/tdex_feeder.go similarity index 100% rename from internal/domain/tdexFeeder.go rename to internal/domain/tdex_feeder.go diff --git a/internal/ports/krakenWebSocket.go b/internal/ports/kraken_websocket.go similarity index 100% rename from internal/ports/krakenWebSocket.go rename to internal/ports/kraken_websocket.go diff --git a/internal/ports/krakenWebSocket_test.go b/internal/ports/kraken_websocket_test.go similarity index 100% rename from internal/ports/krakenWebSocket_test.go rename to internal/ports/kraken_websocket_test.go diff --git a/internal/ports/tdexDaemonPriceUpdater.go b/internal/ports/tdex_daemon_price_updater.go similarity index 100% rename from internal/ports/tdexDaemonPriceUpdater.go rename to internal/ports/tdex_daemon_price_updater.go From 0af52b9c19a5bc5a3503b9d8bac0f8668cb5609a Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 11:03:15 +0100 Subject: [PATCH 63/70] notify interupt channel if SIGTERM / SIGINT occur --- cmd/feederd/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index e0cf60c..066cb77 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "os/signal" + "syscall" log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/adapters" @@ -22,7 +23,7 @@ const ( func main() { // Interrupt Notification. interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, os.Interrupt) + signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) // retrieve feeder service from config file envConfigPath := os.Getenv(envConfigPathKey) From b2e21734d905da421ec83926a957068eac343051 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 13:20:58 +0100 Subject: [PATCH 64/70] add viper --- go.mod | 1 + go.sum | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 258 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 93dd10e..d7d8e5f 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.15 require ( github.com/aopoltorzhicky/go_kraken/websocket v0.0.10 github.com/sirupsen/logrus v1.7.0 + github.com/spf13/viper v1.7.1 github.com/stretchr/testify v1.6.1 github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77 google.golang.org/grpc v1.33.2 diff --git a/go.sum b/go.sum index dd0f2db..1712132 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,69 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/aopoltorzhicky/go_kraken v0.0.4 h1:E+G+6Zt3dmvABvp0GOQGhwAJRB4fvvvmr/GwLnnfsAs= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aopoltorzhicky/go_kraken/rest v0.0.3 h1:oTRp0xqqsq0g83UOu6dCQtbKTk1lZjqtH3TsaDwDt/o= github.com/aopoltorzhicky/go_kraken/rest v0.0.3/go.mod h1:cen8hPWBicFQ1T4EoseSAkxvCD7zzZYIzxb0OVTHDk0= github.com/aopoltorzhicky/go_kraken/websocket v0.0.10 h1:3oOS3BIiPXjtuAJ+Yzfi7WjwA6eA7jyu1ESJ3jvxbp8= github.com/aopoltorzhicky/go_kraken/websocket v0.0.10/go.mod h1:keiaKS3AsahdTS/QD2b220DGzWV/t4tWIjmy+4EruHI= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -26,64 +72,259 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77 h1:njOphJsTU5qCJROyzw4ReARDbucTCbfj5t7Mgnnt9u0= github.com/tdex-network/tdex-protobuf v0.0.0-20201029153650-f5164a4b6a77/go.mod h1:tVWv01BSMH/neJOsixdDFU5T0ll0OZbPliLXBsYQjA8= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -98,9 +339,24 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= From 78e02e4ccd5209502abc8e37664e808b9c746a4f Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 13:21:30 +0100 Subject: [PATCH 65/70] handle default config file for integration test --- cmd/feederd/config.test.json | 1 + cmd/feederd/main.go | 12 ++------ cmd/feederd/main_test.go | 5 ++-- config/config.go | 54 ++++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 12 deletions(-) create mode 100755 cmd/feederd/config.test.json create mode 100644 config/config.go diff --git a/cmd/feederd/config.test.json b/cmd/feederd/config.test.json new file mode 100755 index 0000000..1854090 --- /dev/null +++ b/cmd/feederd/config.test.json @@ -0,0 +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 diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index 066cb77..6fe0c31 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -11,26 +11,18 @@ import ( "syscall" log "github.com/sirupsen/logrus" + "github.com/tdex-network/tdex-feeder/config" "github.com/tdex-network/tdex-feeder/internal/adapters" "github.com/tdex-network/tdex-feeder/internal/application" ) -const ( - envConfigPathKey = "FEEDER_CONFIG_PATH" - defaultConfigPath = "./config.json" -) - func main() { // Interrupt Notification. interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM, syscall.SIGINT) // retrieve feeder service from config file - envConfigPath := os.Getenv(envConfigPathKey) - if envConfigPath == "" { - envConfigPath = defaultConfigPath - } - feeder := configFileToFeederService(envConfigPath) + feeder := configFileToFeederService(config.GetConfigPath()) log.Info("Start the feeder") go func() { diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go index 40e7cad..a270b96 100644 --- a/cmd/feederd/main_test.go +++ b/cmd/feederd/main_test.go @@ -12,11 +12,12 @@ import ( "testing" "time" + "github.com/tdex-network/tdex-feeder/config" "github.com/tdex-network/tdex-feeder/internal/adapters" ) const ( - containerName = "tdexFeederContainerTest" + containerName = "tdexd-feeder-test" daemonEndpoint = "127.0.0.1:9000" krakenWsEndpoint = "ws.kraken.com" // nigiriUrl = "https://nigiri.network/liquid/api" @@ -58,7 +59,7 @@ func runDaemonAndInitConfigFile(t *testing.T) { t.Error(err) } - err = ioutil.WriteFile(defaultConfigPath, bytes, os.ModePerm) + err = ioutil.WriteFile(config.GetConfigPath(), bytes, os.ModePerm) if err != nil { t.Error(err) } diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..09bfb2d --- /dev/null +++ b/config/config.go @@ -0,0 +1,54 @@ +package config + +import ( + "errors" + "log" + "os" + + "github.com/spf13/viper" +) + +const ( + // ConfigFilePathKey is the location of the config.json file. + ConfigFilePathKey = "CONFIG_PATH" + // LogLevelKey ... + LogLevelKey = "LOG_LEVEL" +) + +var vip *viper.Viper + +func init() { + vip = viper.New() + vip.SetEnvPrefix("FEEDER") + vip.AutomaticEnv() + + vip.SetDefault(LogLevelKey, 4) + vip.SetDefault(ConfigFilePathKey, "./config.json") + + validate() +} + +func GetConfigPath() string { + return vip.GetString(ConfigFilePathKey) +} + +func validate() { + if err := validatePath(vip.GetString(ConfigFilePathKey)); err != nil { + log.Fatal(err) + } +} + +func validatePath(path string) error { + if path != "" { + stat, err := os.Stat(path) + if err != nil { + return err + } + + if stat.IsDir() { + return errors.New("not a file") + } + } + + return nil +} From f7863ad4bcb742c93e0d1b072c616582adaa1f08 Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 13:23:26 +0100 Subject: [PATCH 66/70] add viper env vars in Makefile --- Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Makefile b/Makefile index 862c278..a3ca052 100644 --- a/Makefile +++ b/Makefile @@ -41,10 +41,12 @@ help: ## run-linux: Run locally with default configuration run-linux: clean build-linux + export FEEDER_LOG_LEVEL=5; \ ./build/feederd-linux-amd64 ## run-mac: Run locally with default configuration run-mac: clean build-mac + export FEEDER_LOG_LEVEL=5; \ ./build/feederd-darwin-amd64 ## vet: code analysis @@ -60,4 +62,6 @@ shorttest: go test -v -count=1 -race -short ./... integrationtest: + export FEEDER_CONFIG_PATH="./config.test.json"; \ + export FEEDER_LOG_LEVEL=5; \ go test -v -count=1 ./cmd/feederd \ No newline at end of file From 4dae3c0e63b601341bcc6ff69b89da8fa5e4734d Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 13:35:27 +0100 Subject: [PATCH 67/70] move tdex_feeder from domain layer to application one --- config/config.go | 4 ++- internal/application/feed_service_test.go | 10 +----- internal/application/feeder_service.go | 4 +-- .../{domain => application}/tdex_feeder.go | 34 ++++++++++++++++--- .../tdex_feeder_test.go} | 28 +++++++-------- internal/application/test_utils.go | 11 ++++++ internal/domain/feed.go | 32 ++--------------- 7 files changed, 61 insertions(+), 62 deletions(-) rename internal/{domain => application}/tdex_feeder.go (59%) rename internal/{domain/feeder_test.go => application/tdex_feeder_test.go} (61%) create mode 100644 internal/application/test_utils.go diff --git a/config/config.go b/config/config.go index 09bfb2d..e9bdebe 100644 --- a/config/config.go +++ b/config/config.go @@ -23,9 +23,11 @@ func init() { vip.AutomaticEnv() vip.SetDefault(LogLevelKey, 4) - vip.SetDefault(ConfigFilePathKey, "./config.json") validate() + + // this skip the check for default config file (avoid make test fail) + vip.SetDefault(ConfigFilePathKey, "./config.json") } func GetConfigPath() string { diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go index ad11cbd..5f1227e 100644 --- a/internal/application/feed_service_test.go +++ b/internal/application/feed_service_test.go @@ -31,7 +31,7 @@ func TestKrakenFeedService(t *testing.T) { feed := svc.GetFeed() target := &mockTarget{marketPrices: []domain.MarketPrice{}} - feeder := domain.NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) + feeder := NewTdexFeeder([]domain.Feed{feed}, []domain.Target{target}) go func() { err := feeder.Start() if err != nil { @@ -44,11 +44,3 @@ func TestKrakenFeedService(t *testing.T) { assert.Equal(t, true, len(target.marketPrices) > 0) } - -type mockTarget struct { - marketPrices []domain.MarketPrice -} - -func (t *mockTarget) Push(marketPrice domain.MarketPrice) { - t.marketPrices = append(t.marketPrices, marketPrice) -} diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go index 84651a5..2d149c9 100644 --- a/internal/application/feeder_service.go +++ b/internal/application/feeder_service.go @@ -13,7 +13,7 @@ type FeederService interface { } type feederService struct { - tdexFeeder domain.TdexFeeder + tdexFeeder TdexFeeder krakenService FeedService target *TdexDaemonTarget } @@ -33,7 +33,7 @@ func NewFeederService(args NewFeederServiceArgs) FeederService { log.Fatal(err) } - feeder := domain.NewTdexFeeder( + feeder := NewTdexFeeder( []domain.Feed{krakenFeedService.GetFeed()}, []domain.Target{target}, ) diff --git a/internal/domain/tdex_feeder.go b/internal/application/tdex_feeder.go similarity index 59% rename from internal/domain/tdex_feeder.go rename to internal/application/tdex_feeder.go index 1add52b..74779a4 100644 --- a/internal/domain/tdex_feeder.go +++ b/internal/application/tdex_feeder.go @@ -1,8 +1,10 @@ -package domain +package application import ( "errors" "sync" + + "github.com/tdex-network/tdex-feeder/internal/domain" ) type TdexFeeder interface { @@ -12,14 +14,14 @@ type TdexFeeder interface { } type tdexFeeder struct { - feeds []Feed - targets []Target + feeds []domain.Feed + targets []domain.Target stopChan chan bool running bool locker sync.Locker } -func NewTdexFeeder(feeds []Feed, targets []Target) TdexFeeder { +func NewTdexFeeder(feeds []domain.Feed, targets []domain.Target) TdexFeeder { return &tdexFeeder{ feeds: feeds, targets: targets, @@ -63,3 +65,27 @@ func (t *tdexFeeder) IsRunning() bool { defer t.locker.Unlock() return t.running } + +// merge gathers several feeds into a unique channel +func merge(feeds ...domain.Feed) <-chan domain.MarketPrice { + mergedChan := make(chan domain.MarketPrice) + var wg sync.WaitGroup + + wg.Add(len(feeds)) + for _, feed := range feeds { + c := feed.GetMarketPriceChan() + go func(c <-chan domain.MarketPrice) { + for marketPrice := range c { + mergedChan <- marketPrice + } + wg.Done() + }(c) + } + + go func() { + wg.Wait() + close(mergedChan) + }() + + return mergedChan +} diff --git a/internal/domain/feeder_test.go b/internal/application/tdex_feeder_test.go similarity index 61% rename from internal/domain/feeder_test.go rename to internal/application/tdex_feeder_test.go index 0a3ebf6..49fb4b2 100644 --- a/internal/domain/feeder_test.go +++ b/internal/application/tdex_feeder_test.go @@ -1,28 +1,32 @@ -package domain +package application import ( "testing" "time" "github.com/stretchr/testify/assert" + "github.com/tdex-network/tdex-feeder/internal/domain" ) func TestFeeder(t *testing.T) { - feed := NewFeed() - feedBis := NewFeed() + feed := domain.NewFeed() + feedBis := domain.NewFeed() target := &mockTarget{ - marketPrices: make([]MarketPrice, 0), + marketPrices: make([]domain.MarketPrice, 0), } - feeder := NewTdexFeeder([]Feed{feed, feedBis}, []Target{target}) + feeder := NewTdexFeeder( + []domain.Feed{feed, feedBis}, + []domain.Target{target}, + ) - marketPrice := MarketPrice{ - Market: Market{ + marketPrice := domain.MarketPrice{ + Market: domain.Market{ BaseAsset: "1111", QuoteAsset: "0000", }, - Price: Price{ + Price: domain.Price{ BasePrice: 0.2, QuotePrice: 1, }, @@ -53,11 +57,3 @@ func TestFeeder(t *testing.T) { assert.Equal(t, 15, len(target.marketPrices)) } - -type mockTarget struct { - marketPrices []MarketPrice -} - -func (t *mockTarget) Push(marketPrice MarketPrice) { - t.marketPrices = append(t.marketPrices, marketPrice) -} diff --git a/internal/application/test_utils.go b/internal/application/test_utils.go new file mode 100644 index 0000000..3245c13 --- /dev/null +++ b/internal/application/test_utils.go @@ -0,0 +1,11 @@ +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/domain/feed.go b/internal/domain/feed.go index 0756b6b..801c68d 100644 --- a/internal/domain/feed.go +++ b/internal/domain/feed.go @@ -1,9 +1,5 @@ package domain -import ( - "sync" -) - type MarketPrice struct { Market Market Price Price @@ -12,7 +8,7 @@ type MarketPrice struct { // Feed represents a source of MarketPrice data type Feed interface { AddMarketPrice(marketPrice MarketPrice) - getMarketPriceChan() <-chan MarketPrice + GetMarketPriceChan() <-chan MarketPrice } type feed struct { @@ -31,30 +27,6 @@ func (f feed) AddMarketPrice(marketPrice MarketPrice) { f.marketPriceChan <- marketPrice } -// merge gathers several feeds into a unique channel -func merge(feeds ...Feed) <-chan MarketPrice { - mergedChan := make(chan MarketPrice) - var wg sync.WaitGroup - - wg.Add(len(feeds)) - for _, feed := range feeds { - c := feed.getMarketPriceChan() - go func(c <-chan MarketPrice) { - for marketPrice := range c { - mergedChan <- marketPrice - } - wg.Done() - }(c) - } - - go func() { - wg.Wait() - close(mergedChan) - }() - - return mergedChan -} - -func (f feed) getMarketPriceChan() <-chan MarketPrice { +func (f feed) GetMarketPriceChan() <-chan MarketPrice { return f.marketPriceChan } From b2ce17e05a1235633141f9e494b917bbb8a447da Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 14:00:40 +0100 Subject: [PATCH 68/70] add logs --- cmd/feederd/main.go | 4 ++-- go.mod | 1 + go.sum | 4 ++++ internal/application/feed_service.go | 6 +++--- internal/application/tdex_feeder.go | 5 ++++- internal/application/updater_service.go | 8 ++++---- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/cmd/feederd/main.go b/cmd/feederd/main.go index 6fe0c31..57adfeb 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -24,7 +24,7 @@ func main() { // retrieve feeder service from config file feeder := configFileToFeederService(config.GetConfigPath()) - log.Info("Start the feeder") + log.Info("Start the feeder...") go func() { err := feeder.Start() if err != nil { @@ -34,7 +34,7 @@ func main() { // check for interupt <-interrupt - log.Info("Shutting down the feeder") + log.Info("Shutting down the feeder...") err := feeder.Stop() log.Info("Feeder service stopped") if err != nil { diff --git a/go.mod b/go.mod index d7d8e5f..f46f8c1 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ 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/go.sum b/go.sum index 1712132..231b689 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,9 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aopoltorzhicky/go_kraken/rest v0.0.3 h1:oTRp0xqqsq0g83UOu6dCQtbKTk1lZjqtH3TsaDwDt/o= github.com/aopoltorzhicky/go_kraken/rest v0.0.3/go.mod h1:cen8hPWBicFQ1T4EoseSAkxvCD7zzZYIzxb0OVTHDk0= @@ -165,6 +167,7 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -339,6 +342,7 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go index d39e4aa..ac739b2 100644 --- a/internal/application/feed_service.go +++ b/internal/application/feed_service.go @@ -49,7 +49,7 @@ func NewKrakenFeedService( // when start, the services is listening for new data from kraken server func (f *krakenFeedService) Start() { listening := true - log.Println("Start listening kraken service") + log.Info("Kraken web socket feed is listening") tickerWithPriceChan, err := f.krakenWebSocket.StartListen() if err != nil { log.Fatal(err) @@ -63,10 +63,10 @@ func (f *krakenFeedService) Start() { log.Fatal(err) } - log.Info("Feed service stopped") + log.Info("Kraken web socket feed stopped") break case tickerWithPrice := <-tickerWithPriceChan: - log.Debug("Kraken message = " + string(tickerWithPrice.Ticker)) + log.Debug("Kraken web socket receive message = " + string(tickerWithPrice.Ticker)) market, ok := f.tickersToMarketMap[tickerWithPrice.Ticker] if !ok { diff --git a/internal/application/tdex_feeder.go b/internal/application/tdex_feeder.go index 74779a4..f63c4cd 100644 --- a/internal/application/tdex_feeder.go +++ b/internal/application/tdex_feeder.go @@ -4,6 +4,7 @@ import ( "errors" "sync" + log "github.com/sirupsen/logrus" "github.com/tdex-network/tdex-feeder/internal/domain" ) @@ -47,8 +48,10 @@ func (t *tdexFeeder) Start() error { t.running = false break case marketPrice := <-marketPriceChannel: - for _, target := range t.targets { + log.Info("New market price: Market = ", marketPrice.Market, " | Price = ", marketPrice.Price) + for index, target := range t.targets { target.Push(marketPrice) + log.Debug("Pushed to target ", index) } } } diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go index 3cc408b..5aa6a78 100644 --- a/internal/application/updater_service.go +++ b/internal/application/updater_service.go @@ -14,7 +14,7 @@ import ( type TdexDaemonTarget struct { Endpoint string priceUpdater ports.TdexDaemonPriceUpdater - priceUpdaterLocker *sync.RWMutex + priceUpdaterLocker sync.Locker marketsToUpdate map[domain.Market]domain.Price closeChan chan bool } @@ -35,7 +35,7 @@ func NewTdexDaemonTarget( tdexTarget := &TdexDaemonTarget{ Endpoint: tdexDaemonOperatorInterfaceEnpoint, priceUpdater: ports.NewTdexDaemonPriceUpdater(context.Background(), tdexDaemonOperatorInterfaceEnpoint), - priceUpdaterLocker: &sync.RWMutex{}, + priceUpdaterLocker: &sync.Mutex{}, closeChan: make(chan bool, 1), marketsToUpdate: make(map[domain.Market]domain.Price), } @@ -73,8 +73,8 @@ func (daemon *TdexDaemonTarget) Stop() { } func (daemon *TdexDaemonTarget) updatePrice(market domain.Market) { - daemon.priceUpdaterLocker.RLock() - defer daemon.priceUpdaterLocker.RUnlock() + daemon.priceUpdaterLocker.Lock() + defer daemon.priceUpdaterLocker.Unlock() price, ok := daemon.marketsToUpdate[market] if ok { From 062e317f747d8815093b23810402246c819fd00c Mon Sep 17 00:00:00 2001 From: louisinger Date: Thu, 10 Dec 2020 14:53:58 +0100 Subject: [PATCH 69/70] format test config --- cmd/feederd/config.test.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/feederd/config.test.json b/cmd/feederd/config.test.json index 1854090..0278506 100755 --- a/cmd/feederd/config.test.json +++ b/cmd/feederd/config.test.json @@ -1 +1,10 @@ -{"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":"bffce3908a595436b6ab08f916fea2c9fc6a702f46b268ca354205d127f60c48","kraken_ticker":"LTC/USDT", + "interval":500 + } + ] +} \ No newline at end of file From dbc56728de85507bffd62f90ee7999e3a32e5396 Mon Sep 17 00:00:00 2001 From: Marco Argentieri <3596602+tiero@users.noreply.github.com> Date: Thu, 10 Dec 2020 16:04:08 +0100 Subject: [PATCH 70/70] Update internal/application/tdex_feeder.go --- internal/application/tdex_feeder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/application/tdex_feeder.go b/internal/application/tdex_feeder.go index f63c4cd..6594ad5 100644 --- a/internal/application/tdex_feeder.go +++ b/internal/application/tdex_feeder.go @@ -48,7 +48,7 @@ func (t *tdexFeeder) Start() error { t.running = false break case marketPrice := <-marketPriceChannel: - log.Info("New market price: Market = ", marketPrice.Market, " | Price = ", marketPrice.Price) + log.Info("Market ", marketPrice.Market.BaseAsset[:4], "-", marketPrice.Market.QuoteAsset[:4], " | Base Price ", marketPrice.Price.BasePrice, " | Quote Price ", marketPrice.Price.QuotePrice) for index, target := range t.targets { target.Push(marketPrice) log.Debug("Pushed to target ", index)