diff --git a/Makefile b/Makefile index bace101..2b41f3f 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ .PHONY: build run clean cov fmt help vet test shorttest integrationtest -## build-arm: build binary for ARM +## build: builds binary for all platforms build: chmod u+x ./scripts/build ./scripts/build @@ -15,7 +15,7 @@ cov: @echo "Coverage..." go test -cover ./... -## fmt: Go Format +## fmt: checks if code is well formatted fmt: @echo "Gofmt..." @if [ -n "$(gofmt -l .)" ]; then echo "Go code is not formatted"; exit 1; fi @@ -26,24 +26,22 @@ help: @echo "Usage: \n" @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' -## run-linux: Run locally with default configuration +# run: runs locally without building binary run: clean - export FEEDER_CONFIG_PATH=./config.example.json; \ export FEEDER_LOG_LEVEL=5; \ go run cmd/feederd/main.go ## vet: code analysis vet: - @echo "Vet..." + @echo "Vetting..." @go vet ./... -## test: runs go unit test with default values -test: fmt shorttest - -shorttest: +## test: runs unit tests +test: fmt @echo "Testing..." go test -v -count=1 -race -short ./... +## integrationtest: run integration tests integrationtest: export FEEDER_CONFIG_PATH="./config.test.json"; \ export FEEDER_LOG_LEVEL=5; \ diff --git a/README.md b/README.md index a5edbdc..f6785ff 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,7 @@ Feeder allows to connect several price feeds to TDex Daemon(s) in order to autom ## Overview -tdex-feeder connects to exchanges and retrieves market prices in order to consume the gRPC -interface exposed from tdex-deamon `UpdateMarketPrice`. - +tdex-feeder connects to exchanges and forwards market(s) price feeds to some tdex-daemon(s) by consuming its `UpdateMarketPrice` RPC. ![tdex-schema](./tdexfeeder.png) ## ⬇️ Run Standalone @@ -58,31 +56,36 @@ the `$HOME/config.json` is the path to the feederd configuration file. ### Build it yourself -Builds feeder as static binary and runs the project with default configuration. - -#### Linux - -`make build-linux` - -#### Mac +Build feeder as static binary and run the project with default configuration. -`make build-mac` +```sh +# build feeder as a static binary +$ make build -#### Run Linux +# run the binary on Linux +$ ./build/feederd-linux-amd64 -`make run-linux` +# run the binary on MacOS +$ ./build/feederd-darwin-amd64 +``` ##### Config file Rename the file `./config.example.json` into `./config.json` and adapt if for your specific purpose. The default example -connects to kraken socket and to a local instance of tdex-deamon. +uses kraken as price feeder and forwards the feeds related to the BTC/USDT market to a local daemon (with mac auth disabled). + +NOTE: All entries of the JSON configuration file are mandatory if not otherwise expressed. Below you can find a brief explanation of the JSON object. ``` -daemon_endpoint: String with the address and port of gRPC host. Required. -market: 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. -``` +price_feeder: service where to source prices (only "kraken" available for now). +interval: the period in millisecond with which the feeder updates its target(s). +markets: list with necessary markets info. + base_asset: hex string of the hash of the market base asset. + quote_asset: hex string of the hash of the market quote asset. + ticker: string of the corresponding feeder's market ticker. + targets: list of daemons to update for every feed received on this market. + rpc_address: string with hostname and port of the target daemon. + tls_cert_path: string path of the TLS cert file to use to connect with the target daemon. Optional. + macaroons_path: string path of the macaroon file to use to connect with the target daemon. Optional. +``` \ No newline at end of file diff --git a/cmd/feederd/config.test.json b/cmd/feederd/config.test.json deleted file mode 100755 index 917706c..0000000 --- a/cmd/feederd/config.test.json +++ /dev/null @@ -1 +0,0 @@ -{"daemon_endpoint":"127.0.0.1:9000","markets":[{"base_asset":"5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225","quote_asset":"25e4fac326a58791c64e31b29d446ac6a6ea5f52ab3a62b12596c7abc34f35a6","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 49736f0..b1769c8 100644 --- a/cmd/feederd/main.go +++ b/cmd/feederd/main.go @@ -1,70 +1,85 @@ -// Copyright (c) 2020 The VulpemVentures developers - -// Feeder allows to connect an external price feed to the TDex Daemon to determine the current market price. package main import ( - "encoding/json" - "io/ioutil" "os" "os/signal" "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" -) - -func main() { - // Interrupt Notification. - interrupt := make(chan os.Signal, 1) - signal.Notify(interrupt, syscall.SIGTERM, syscall.SIGINT) - // retrieve feeder service from config file - feeder := configFileToFeederService(config.GetConfigPath()) + "github.com/tdex-network/tdex-feeder/internal/config" + "github.com/tdex-network/tdex-feeder/internal/core/application" + grpcclient "github.com/tdex-network/tdex-feeder/internal/core/infrastructure/client/grpc" + krakenfeeder "github.com/tdex-network/tdex-feeder/internal/core/infrastructure/feeder/kraken" + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) - log.Info("Start the feeder...") - go func() { - err := feeder.Start() - if err != nil { - log.Fatal(err) - } - }() +type indexedPriceFeeders map[string]func(args ...interface{}) (ports.PriceFeeder, error) - // check for interupt - <-interrupt - log.Info("Shutting down the feeder...") - err := feeder.Stop() - log.Info("Feeder service stopped") - if err != nil { - log.Fatal(err) +func (i indexedPriceFeeders) supported() []string { + keys := make([]string, 0, len(i)) + for k := range i { + keys = append(keys, k) } - os.Exit(0) + return keys } -func configFileToFeederService(configFilePath string) application.FeederService { - jsonFile, err := os.Open(configFilePath) - if err != nil { - log.Fatal(err) +var ( + priceFeeders = indexedPriceFeeders{ + "kraken": krakenfeeder.NewKrakenPriceFeeder, } - defer jsonFile.Close() +) - configBytes, err := ioutil.ReadAll(jsonFile) +func main() { + cfg, err := config.NewConfigFromFile() if err != nil { - log.Fatal(err) + log.WithError(err).Fatalf( + "error while reading config from file %s", config.GetConfigPath(), + ) } - config := &adapters.Config{} - err = json.Unmarshal(configBytes, config) - if err != nil { - log.Fatal(err) + priceFeederFactory, ok := priceFeeders[cfg.PriceFeeder] + if !ok { + log.Fatalf( + "price feeder must be one of: '%s'", priceFeeders.supported(), + ) } - feeder, err := config.ToFeederService() + priceFeeder, err := priceFeederFactory(cfg.Interval, cfg.PortableMarkets()) if err != nil { - log.Fatal(err) + log.WithError(err).Fatal("error while initializing price feeder") + } + + indexedTargets := make(application.IndexedTargetsByMarket) + for _, mkt := range cfg.Markets { + targets := make(map[string]ports.TdexClient) + for _, t := range mkt.CTargets { + target, err := grpcclient.NewGRPCClient(t.RPCAddress, t.MacaroonsPath, t.TLSCertPath) + if err != nil { + log.WithError(err).Fatalf( + "error while connecting with target %s", t.RPCAddress, + ) + } + targets[target.RPCAddress()] = target + } + mktKey := ports.MarketKey(mkt) + indexedTargets[mktKey] = targets } - return feeder + app := application.NewService(priceFeeder, indexedTargets) + + defer app.Stop() + + log.Info("starting service") + go func() { + if err := app.Start(); err != nil { + log.WithError(err).Fatal("service exited with error") + } + }() + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT) + <-sigChan + + log.Info("shutting down") } diff --git a/cmd/feederd/main_test.go b/cmd/feederd/main_test.go deleted file mode 100644 index f7998ae..0000000 --- a/cmd/feederd/main_test.go +++ /dev/null @@ -1,225 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "net/http" - "os" - "os/exec" - "testing" - "time" - - "github.com/tdex-network/tdex-feeder/config" - "github.com/tdex-network/tdex-feeder/internal/adapters" -) - -const ( - containerName = "tdexd-feeder-test" - daemonEndpoint = "127.0.0.1:9000" - krakenWsEndpoint = "ws.kraken.com" - // nigiriUrl = "https://nigiri.network/liquid/api" - nigiriURL = "http://localhost:3001" - password = "vulpemsecret" -) - -func TestFeeder(t *testing.T) { - if testing.Short() { - t.Skip("skipping test in short mode.") - } - - runDaemonAndInitConfigFile(t) - t.Cleanup(stopAndDeleteContainer) - - go main() - time.Sleep(30 * time.Second) - os.Exit(0) -} - -func runDaemonAndInitConfigFile(t *testing.T) { - usdt := runDaemonAndCreateMarket(t) - - configJSON := adapters.ConfigJSON{ - DaemonEndpoint: daemonEndpoint, - Markets: []adapters.MarketJSON{ - adapters.MarketJSON{ - KrakenTicker: "LTC/USDT", - BaseAsset: "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", - QuoteAsset: usdt, - Interval: 500, - }, - }, - } - - bytes, err := json.Marshal(configJSON) - if err != nil { - t.Error(err) - } - - err = ioutil.WriteFile(config.GetConfigPath(), 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", - "--network=host", - "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 - } - - if resp.StatusCode != 200 { - return "", "", errors.New("Internal server error") - } - - 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 - } - - if respBody["asset"].(string) == "" { - return mint(address, amount) - } - - 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/config.example.json b/config.example.json old mode 100755 new mode 100644 index 684178b..0a8d59e --- a/config.example.json +++ b/config.example.json @@ -1,13 +1,18 @@ { - "daemon_endpoint": "127.0.0.1:9000", - "macaroons_path": "", - "tls_cert_path": "", + "price_feeder": "kraken", + "interval": 1000, "markets": [ { "base_asset": "5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225", - "quote_asset": "e3ec88b0ed9228c3337f746f5d9ea20c1e68bbe1f73d94621c1e8452bebb9e22", - "kraken_ticker": "XBT/USDT", - "interval": 500 + "quote_asset": "e2139463404c83ac2528527e612ff68174d6d0995fac7cf49e7711624b46c235", + "ticker": "XBT/USDT", + "targets": [ + { + "rpc_address": "localhost:9000", + "tls_cert_path": "", + "macaroons_path": "" + } + ] } ] } \ No newline at end of file diff --git a/config/config.go b/config/config.go deleted file mode 100644 index 3ca2689..0000000 --- a/config/config.go +++ /dev/null @@ -1,58 +0,0 @@ -package config - -import ( - "errors" - "os" - - log "github.com/sirupsen/logrus" - "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) - - err := validate() - if err != nil { - log.Fatalf("invalid config: %s", err) - } - - // this skip the check for default config file (avoid make test fail) - vip.SetDefault(ConfigFilePathKey, "./config.json") -} - -// GetConfigPath return the path of the config.json file -func GetConfigPath() string { - return vip.GetString(ConfigFilePathKey) -} - -func validate() error { - return validatePath(vip.GetString(ConfigFilePathKey)) -} - -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 -} diff --git a/go.mod b/go.mod index 66ba47e..48b3cc3 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,13 @@ go 1.15 require ( github.com/gorilla/websocket v1.4.2 - github.com/sirupsen/logrus v1.7.0 + github.com/shopspring/decimal v1.2.0 + github.com/sirupsen/logrus v1.8.1 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 + github.com/stretchr/testify v1.7.0 + github.com/tdex-network/tdex-daemon v0.5.4-0.20210901130622-99c797e9450a + github.com/tdex-network/tdex-daemon/pkg/macaroons v0.0.0-20210813140257-70d50a8b72a4 + github.com/tdex-network/tdex-protobuf v0.0.0-20210507104156-d509331cccdb + google.golang.org/grpc v1.40.0 gopkg.in/macaroon.v2 v2.1.0 ) diff --git a/go.sum b/go.sum index 02d24fc..2ecac0f 100644 --- a/go.sum +++ b/go.sum @@ -5,82 +5,188 @@ cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6A 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 v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 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/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 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/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= +github.com/btcsuite/btcd v0.21.0-beta.0.20201208033208-6bd4c64a54fa/go.mod h1:Sv4JPQ3/M+teHz9Bo5jBpkNcP0x6r7rdihlNL/7tTAs= +github.com/btcsuite/btcd v0.21.0-beta.0.20210426180113-7eba688b65e5/go.mod h1:9n5ntfhhHQBIhUvlhDvD3Qg6fRUj4jkN0VB8L8svzOA= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= +github.com/btcsuite/btcutil/psbt v1.0.2/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= +github.com/btcsuite/btcutil/psbt v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:LVveMu4VaNSkIRTZu2+ut0HDBRuYjqGocxDMNS1KuGQ= +github.com/btcsuite/btcwallet v0.12.0 h1:0kT0rDN8vNcAvuHp2qUS/hLsfa0VUn2dNQ2GvM9ozBA= +github.com/btcsuite/btcwallet v0.12.0/go.mod h1:f1HuBGov5+OTp40Gh1vA+tvF6d7bbuLFTceJMRB7fXw= +github.com/btcsuite/btcwallet/wallet/txauthor v1.0.0/go.mod h1:VufDts7bd/zs3GV13f/lXc/0lXrPnvxD/NvmpG/FEKU= +github.com/btcsuite/btcwallet/wallet/txrules v1.0.0/go.mod h1:UwQE78yCerZ313EXZwEiu3jNAtfXj2n2+c8RWiE/WNA= +github.com/btcsuite/btcwallet/wallet/txsizes v1.0.0/go.mod h1:pauEU8UuMFiThe5PB3EO+gO5kx87Me5NvdQDsTuq6cs= +github.com/btcsuite/btcwallet/walletdb v1.3.4/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= +github.com/btcsuite/btcwallet/walletdb v1.3.5 h1:SoxUPLgJUkyO1XqON6X7x+rjHJoIpRQov8o8X6gNoz8= +github.com/btcsuite/btcwallet/walletdb v1.3.5/go.mod h1:oJDxAEUHVtnmIIBaa22wSBPTVcs6hUp5NKWmI8xDwwU= +github.com/btcsuite/btcwallet/wtxmgr v1.3.0/go.mod h1:awQsh1n/0ZrEQ+JZgWvHeo153ubzEisf/FyNtwI0dDk= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 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/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 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/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= +github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= 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/frankban/quicktest v1.0.0 h1:QgmxFbprE29UG4oL88tGiiL/7VuiBl5xCcz+wJcJhc0= github.com/frankban/quicktest v1.0.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= +github.com/frankban/quicktest v1.1.0/go.mod h1:R98jIehRai+d1/3Hv2//jOVCTJhW1VBavT6B6CuGq2k= +github.com/frankban/quicktest v1.2.2/go.mod h1:Qh/WofXFeiAFII1aEBu529AtJo6Zg2VHscnEsbBnJ20= +github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= 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-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 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-macaroon-bakery/macaroonpb v1.0.0 h1:It9exBaRMZ9iix1iJ6gwzfwsDE6ExNuwtAJ9e09v6XE= +github.com/go-macaroon-bakery/macaroonpb v1.0.0/go.mod h1:UzrGOcbiwTXISFP2XDLDPjfhMINZa+fX/7A2lMd31zc= 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-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= 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/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 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/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 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/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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.2.1-0.20190312032427-6f77996f0c42/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 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/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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= @@ -90,8 +196,10 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR 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-middleware v1.0.1-0.20190118093823-f849b5445de4/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/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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= @@ -113,29 +221,60 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO 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/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 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/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 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/juju/mgotest v1.0.1/go.mod h1:vTaDufYul+Ps8D7bgseHjq87X8eu0ivlKLp9mVc/Bfc= +github.com/juju/postgrestest v1.1.0/go.mod h1:/n17Y2T6iFozzXwSCO0JYJ5gSiz2caEtSwAjh/uLXDM= +github.com/juju/qthttptest v0.0.1/go.mod h1://LCf/Ls22/rPw2u1yWukUJvYtfPY4nYpWUl2uZhryo= +github.com/juju/schema v1.0.0/go.mod h1:Y+ThzXpUJ0E7NYYocAbuvJ7vTivXfrof/IfRPq/0abI= +github.com/juju/webbrowser v0.0.0-20160309143629-54b8c57083b4/go.mod h1:G6PCelgkM6cuvyD10iYJsjLBsSadVXtJ+nBxFAxE2BU= 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/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kkdai/bstream v0.0.0-20181106074824-b3251f7901ec/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 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/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lightninglabs/gozmq v0.0.0-20191113021534-d20a764486bf/go.mod h1:vxmQPeIQxPf6Jf9rM8R+B4rKBqLA2AjttNxkFBL2Plk= +github.com/lightninglabs/neutrino v0.12.1/go.mod h1:GlKninWpRBbL7b8G0oQ36/8downfnFwKsr0hbRA6E/E= +github.com/lightningnetwork/lnd/clock v1.0.1/go.mod h1:KnQudQ6w0IAMZi1SgvecLZQZ43ra2vpDNj7H/aasemg= +github.com/lightningnetwork/lnd/healthcheck v1.0.0 h1:0Cj8aiFB1OANsUuW9P0OlheSPcZEB38rUQXankr8ZpU= +github.com/lightningnetwork/lnd/healthcheck v1.0.0/go.mod h1:u92p1JGFJNMSkMvztKEwmt1P3TRnLeJBXZ3M85xkU1E= +github.com/lightningnetwork/lnd/queue v1.0.1/go.mod h1:vaQwexir73flPW43Mrm7JOgJHmcEFBWWSl9HlyASoms= +github.com/lightningnetwork/lnd/ticker v1.0.0 h1:S1b60TEGoTtCe2A0yeB+ecoj/kkS4qpwh6l+AkQEZwU= +github.com/lightningnetwork/lnd/ticker v1.0.0/go.mod h1:iaLXJiVgI1sPANIF2qYYUJXjoksPNvGNYowB8aRbpX0= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= +github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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-homedir v1.1.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= @@ -146,6 +285,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ 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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 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= @@ -156,69 +299,137 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN 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_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 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/common v0.4.1/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/procfs v0.0.2/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/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 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/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/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/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/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/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 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.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 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 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/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/tdex-network/tdex-daemon v0.2.1-0.20210317215952-93a47434cc41/go.mod h1:fwFH5N+hlzLDxdOlFnEjxS/+yTEuQKTVrJWbeKo2+MA= +github.com/tdex-network/tdex-daemon v0.5.4-0.20210901130622-99c797e9450a h1:7i2URTeFB483sZTaC+drnF4FFduBeiI0/e8s4CWjCAU= +github.com/tdex-network/tdex-daemon v0.5.4-0.20210901130622-99c797e9450a/go.mod h1:apijCp+E5vYfkqt+gFjD7UscPNzeMHa+vkGdnUUldLI= +github.com/tdex-network/tdex-daemon/pkg/explorer v0.0.0-20210322164638-77a31ea9e66d/go.mod h1:4gMUHFqbQCzBULCsmoyuvFKXX1Yr6a6r6qyhlTtR+KM= +github.com/tdex-network/tdex-daemon/pkg/macaroons v0.0.0-20210813140257-70d50a8b72a4 h1:bS5GUhGo6/xCJNgYlxDAb5mNxaKJRZs0j4SYGpF+bB0= +github.com/tdex-network/tdex-daemon/pkg/macaroons v0.0.0-20210813140257-70d50a8b72a4/go.mod h1:JNwFSeCsjtNtzw4Eltr+sJVQT49d3GA6yGFpBg14WEE= +github.com/tdex-network/tdex-daemon/pkg/securestore v0.0.0-20210603132950-1bfb0312f255/go.mod h1:TkOTBwqf+PjzzYWKwZGLTMfom6vDSbKC91fz2LGKraA= +github.com/tdex-network/tdex-daemon/pkg/securestore v0.0.0-20210813140257-70d50a8b72a4 h1:ILgQOou9xPf4Fdd3viS5FkjQzNRYYAIRw71AWr7Y09w= +github.com/tdex-network/tdex-daemon/pkg/securestore v0.0.0-20210813140257-70d50a8b72a4/go.mod h1:TkOTBwqf+PjzzYWKwZGLTMfom6vDSbKC91fz2LGKraA= +github.com/tdex-network/tdex-protobuf v0.0.0-20210312170501-eac8b4a88d04/go.mod h1:tVWv01BSMH/neJOsixdDFU5T0ll0OZbPliLXBsYQjA8= +github.com/tdex-network/tdex-protobuf v0.0.0-20210507104156-d509331cccdb h1:QfeDbKuqY2nbcTKd/WHAbzcb6SkbRIBFLUHBMtPM5do= +github.com/tdex-network/tdex-protobuf v0.0.0-20210507104156-d509331cccdb/go.mod h1:tVWv01BSMH/neJOsixdDFU5T0ll0OZbPliLXBsYQjA8= +github.com/thanhpk/randstr v1.0.4/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= +github.com/timshannon/badgerhold/v2 v2.0.0-20201016201833-94bc303c76d4/go.mod h1:DZdjzq3LBgyWcoNEDiEbO7weCcZbebdFzeJiap0qwr0= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/vulpemventures/fastsha256 v0.0.0-20160815193821-637e65642941/go.mod h1:GXBJykxW2kUcktGdsgyay7uwwWvkljASfljNcT0mbh8= +github.com/vulpemventures/go-bip39 v1.0.2/go.mod h1:mjFmuv9D6BtANI6iscMmbHhmBOwjMCFfny3mxHnuDrk= +github.com/vulpemventures/go-elements v0.1.1/go.mod h1:OtH2q9NiwybzT9X0AWvkNY+sbxy3okD+vC9oAu5hWhE= +github.com/vulpemventures/go-elements v0.2.1-0.20210409173614-5b1acc1d1e95/go.mod h1:O5QYFgeOmfSPX3shsV+lvEotvzcDFtLoYY0jjdT+sVw= +github.com/vulpemventures/go-secp256k1-zkp v1.1.0/go.mod h1:zo7CpgkuPgoe7fAV+inyxsI9IhGmcoFgyD8nqZaPSOM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= 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-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180723164146-c126467f60eb/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 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-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/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-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 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/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -226,56 +437,123 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl 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/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 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/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20150829230318-ea47fc708ee3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 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-20180906233101-161cd47e91fd/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-20181106065722-10aee1819953/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-20190206173232-65e2d4e15006/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/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/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/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/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20180909124046-d0be0721c37e/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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83 h1:kHSDPqCtsHZOg0nVylfTo20DDhE9gG4Y0jn7hKQ0QAM= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 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/time v0.0.0-20191024005414-555d28b269f0/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-20180828015842-6cd1fcedba52/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-20181008205924-a2b3f7f249e9/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= @@ -292,19 +570,60 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn 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/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/uS+qIUoksaLKe6OS3nUKxOKuIFz1sl2/jx4= 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= @@ -313,44 +632,97 @@ google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98 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-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc/examples v0.0.0-20200925170654-e6c98a478e62/go.mod h1:Lh55/1hxmVHEkOvSIQ2uj0P12QyOCUNyRwnUlSS13hw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 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/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/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk= +gopkg.in/errgo.v1 v1.0.1 h1:oQFRXzZ7CkBGdm1XZm/EbQYaYNNEElNBOd09M6cqNso= +gopkg.in/errgo.v1 v1.0.1/go.mod h1:3NjfXwocQRYAPTq4/fzX+CwUhPRcR/azYRhj8G+LqMo= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/httprequest.v1 v1.2.0/go.mod h1:T61ZUaJLpMnzvoJDO03ZD8yRXD4nZzBeDoW5e9sffjg= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/juju/environschema.v1 v1.0.0/go.mod h1:WTgU3KXKCVoO9bMmG/4KHzoaRvLeoxfjArpgd1MGWFA= +gopkg.in/macaroon-bakery.v2 v2.3.0 h1:b40knPgPTke1QLTE8BSYeH7+R/hiIozB1A8CTLYN0Ic= +gopkg.in/macaroon-bakery.v2 v2.3.0/go.mod h1:/8YhtPARXeRzbpEPLmRB66+gQE8/pzBBkWwg7Vz/guc= gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI= gopkg.in/macaroon.v2 v2.1.0/go.mod h1:OUb+TQP/OP0WOerC2Jp/3CwhIKyIa9kQjuc7H24e6/o= +gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/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-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 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= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/adapters/config_service.go b/internal/adapters/config_service.go deleted file mode 100644 index 3562b5c..0000000 --- a/internal/adapters/config_service.go +++ /dev/null @@ -1,134 +0,0 @@ -package adapters - -import ( - "encoding/json" - "regexp" - "time" - - "github.com/tdex-network/tdex-feeder/internal/application" - "github.com/tdex-network/tdex-feeder/internal/domain" -) - -// MarketJSON is the struct describing the shape of market specs in config JSON file -type MarketJSON struct { - BaseAsset string `json:"base_asset"` - QuoteAsset string `json:"quote_asset"` - KrakenTicker string `json:"kraken_ticker"` - Interval int `json:"interval"` -} - -// ConfigJSON is the struct describing the shape of config JSON file -type ConfigJSON struct { - DaemonEndpoint string `json:"daemon_endpoint"` - MacaroonsPath string `json:"macaroons_path"` - TLSCertPath string `json:"tls_cert_path"` - Markets []MarketJSON `json:"markets"` -} - -// Config is the config of the application retreived from config JSON file -type Config struct { - daemonEndpoint string - macaroonsPath string - tlsCertPath string - markets map[string]domain.Market - marketIntervals map[domain.Market]time.Duration -} - -// ToFeederService transforms a Config into FeederService -func (config *Config) ToFeederService() (application.FeederService, error) { - return application.NewFeederService(application.NewFeederServiceArgs{ - OperatorEndpoint: config.daemonEndpoint, - MacaroonsPath: config.macaroonsPath, - TLSCertPath: config.tlsCertPath, - TickerToMarket: config.markets, - MarketToInterval: config.marketIntervals, - }) -} - -// UnmarshalJSON ... -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.macaroonsPath = jsonConfig.MacaroonsPath - config.tlsCertPath = jsonConfig.TLSCertPath - - configTickerToMarketMap := make(map[string]domain.Market) - marketIntervalsMap := make(map[domain.Market]time.Duration) - - for _, marketJSON := range jsonConfig.Markets { - 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 -} - -func (configJson ConfigJSON) validate() error { - if configJson.DaemonEndpoint == "" { - return ErrDaemonEndpointIsEmpty - } - - if macOK, certOK := - configJson.MacaroonsPath != "", configJson.TLSCertPath != ""; macOK != certOK { - return ErrInvalidAuth - } - - if len(configJson.Markets) == 0 { - return ErrNeedAtLeastOneMarketToFeed - } - - for _, marketJSON := range configJson.Markets { - if marketJSON.KrakenTicker == "" { - return ErrKrakenTickerIsEmpty - } - - err := validateAssetString(marketJSON.BaseAsset) - if err != nil { - return err - } - - err = validateAssetString(marketJSON.QuoteAsset) - if err != nil { - return err - } - - if marketJSON.Interval < 0 { - return ErrIntervalIsNotPositiveNumber - } - } - - 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 ErrInvalidAssetHash{asset: asset} - } - - return nil -} diff --git a/internal/adapters/errors.go b/internal/adapters/errors.go deleted file mode 100644 index 8f2da05..0000000 --- a/internal/adapters/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -package adapters - -import "errors" - -var ( - // ErrDaemonEndpointIsEmpty is returned if the config contains an empty tdex-daemon endpoint - ErrDaemonEndpointIsEmpty = errors.New("daemon endpoint is empty") - // ErrKrakenEndpointIsEmpty is returned if the config contains an empty kraken WS endpoint - ErrKrakenEndpointIsEmpty = errors.New("kraken websocket endpoint is empty") - // ErrNeedAtLeastOneMarketToFeed is returned if the config does not contain market to feed - ErrNeedAtLeastOneMarketToFeed = errors.New("need at least 1 market to feed") - // ErrKrakenTickerIsEmpty is returned if the ticker specified in config is an empty string - ErrKrakenTickerIsEmpty = errors.New("krakenTicker should not be an empty string") - // ErrIntervalIsNotPositiveNumber is returned if the interval is < 0 - ErrIntervalIsNotPositiveNumber = errors.New("interval must be greater (or equal) than 0") - // ErrInvalidAuth is returned in case one between the macaroons and the tls - // certificate paths are missing in case the other is defined. - ErrInvalidAuth = errors.New("both macaroons filepath and TLS cert path must be defined") -) - -// ErrInvalidAssetHash is returned if the given string `asset` is not a valid asset hash string -type ErrInvalidAssetHash struct { - asset string -} - -var _ error = &ErrInvalidAssetHash{} - -func (e ErrInvalidAssetHash) Error() string { - return "the string '" + e.asset + "' is an invalid asset string." -} diff --git a/internal/application/feed_service.go b/internal/application/feed_service.go deleted file mode 100644 index 2f84d7f..0000000 --- a/internal/application/feed_service.go +++ /dev/null @@ -1,89 +0,0 @@ -package application - -import ( - log "github.com/sirupsen/logrus" - - "github.com/tdex-network/tdex-feeder/internal/domain" - "github.com/tdex-network/tdex-feeder/internal/ports" -) - -// FeedService is the interface wrapping krakenWS and transform it into a domain.Feed -type FeedService interface { - Start() error - Stop() error - GetFeed() domain.Feed -} - -type krakenFeedService struct { - feed domain.Feed - krakenWebSocket ports.KrakenWebSocket - tickersToMarketMap map[string]domain.Market -} - -// NewKrakenFeedService is the factory function for FeedService -func NewKrakenFeedService( - address string, - tickersToMarketMap map[string]domain.Market, -) (FeedService, error) { - newFeed := domain.NewFeed() - - tickersToSubscribe := make([]string, 0) - for k := range tickersToMarketMap { - tickersToSubscribe = append(tickersToSubscribe, k) - } - - krakenSocket := ports.NewKrakenWebSocket(tickersToSubscribe) - err := krakenSocket.Connect() - if err != nil { - return nil, err - } - - return &krakenFeedService{ - krakenWebSocket: krakenSocket, - feed: newFeed, - tickersToMarketMap: tickersToMarketMap, - }, nil -} - -// Start is the main function of krakenFeedService -// when start, the services is listening for new data from kraken server -func (f *krakenFeedService) Start() error { - log.Info("Kraken web socket feed is listening") - tickerWithPriceChan, err := f.krakenWebSocket.Start() - if err != nil { - return err - } - - go func() { - for tickerWithPrice := range tickerWithPriceChan { - log.Debug("Kraken web socket receive 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{ - Market: market, - Price: domain.Price{ - BasePrice: 1 / float32(tickerWithPrice.Price), - QuotePrice: float32(tickerWithPrice.Price), - }, - }) - - } - }() - - return nil -} - -// Stop closes the connection with the kraken websocket -func (f *krakenFeedService) Stop() error { - return f.krakenWebSocket.Stop() -} - -// GetFeed is a getter function for kraken's feed member -func (f *krakenFeedService) GetFeed() domain.Feed { - return f.feed -} diff --git a/internal/application/feed_service_test.go b/internal/application/feed_service_test.go deleted file mode 100644 index 75b291d..0000000 --- a/internal/application/feed_service_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package application - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/tdex-network/tdex-feeder/internal/domain" - "github.com/tdex-network/tdex-feeder/pkg/feeder" - "github.com/tdex-network/tdex-feeder/pkg/testutils" -) - -const ( - 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, - QuoteAsset: quoteAsset, - } - - svc, err := NewKrakenFeedService(krakenWsEndpoint, tickerMap) - if err != nil { - t.Fatal(err) - } - err = svc.Start() - if err != nil { - t.Fatal(err) - } - defer svc.Stop() - - feed := svc.GetFeed() - target := &testutils.MockTarget{MarketPrices: []domain.MarketPrice{}} - feeder := feeder.NewFeeder([]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) -} diff --git a/internal/application/feeder_service.go b/internal/application/feeder_service.go deleted file mode 100644 index 30a1323..0000000 --- a/internal/application/feeder_service.go +++ /dev/null @@ -1,76 +0,0 @@ -package application - -import ( - "time" - - "github.com/tdex-network/tdex-feeder/internal/domain" - "github.com/tdex-network/tdex-feeder/pkg/feeder" -) - -// FeederService is a tdex-configured feeder. It takes data from KrakenWS endpoint and update a tdex daemon -type FeederService interface { - Start() error - Stop() error -} - -type feederService struct { - tdexFeeder feeder.Service - krakenService FeedService - target *TdexDaemonTarget -} - -// NewFeederServiceArgs is a wrapper for NewFeederService arguments -type NewFeederServiceArgs struct { - OperatorEndpoint string - MacaroonsPath string - TLSCertPath string - MarketToInterval map[domain.Market]time.Duration - KrakenWSaddress string - TickerToMarket map[string]domain.Market -} - -// NewFeederService is the factory function for the FeederService -func NewFeederService(args NewFeederServiceArgs) (FeederService, error) { - target, err := NewTdexDaemonTarget( - args.OperatorEndpoint, args.MacaroonsPath, args.TLSCertPath, - args.MarketToInterval, - ) - if err != nil { - return nil, err - } - - krakenFeedService, err := NewKrakenFeedService(args.KrakenWSaddress, args.TickerToMarket) - if err != nil { - return nil, err - } - - feeder := feeder.NewFeeder( - []domain.Feed{krakenFeedService.GetFeed()}, - []domain.Target{target}, - ) - - return &feederService{ - tdexFeeder: feeder, - krakenService: krakenFeedService, - target: target.(*TdexDaemonTarget), - }, nil -} - -func (feeder *feederService) Start() error { - err := feeder.krakenService.Start() - if err != nil { - return err - } - - return feeder.tdexFeeder.Start() -} - -func (feeder *feederService) Stop() error { - err := feeder.krakenService.Stop() - if err != nil { - return err - } - feeder.target.Stop() - feeder.tdexFeeder.Stop() - return nil -} diff --git a/internal/application/updater_service.go b/internal/application/updater_service.go deleted file mode 100644 index bf4e876..0000000 --- a/internal/application/updater_service.go +++ /dev/null @@ -1,95 +0,0 @@ -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" -) - -// TdexDaemonTarget implements the domain.Target interface and manage interval for each market -type TdexDaemonTarget struct { - Endpoint string - priceUpdater ports.TdexDaemonPriceUpdater - priceUpdaterLocker sync.Locker - marketsToUpdate map[domain.Market]domain.Price - closeChan chan bool -} - -// NewTdexDaemonTarget configure a tdexDaemonUpdater using the endpoint -// and start goroutines depending of the configured intervals for each market. -func NewTdexDaemonTarget( - tdexOperatorEndpoint, macaroonsPath, tlsCertPath string, - marketToIntervalMap map[domain.Market]time.Duration, -) (domain.Target, error) { - now := time.Now() - mapLastSent := make(map[domain.Market]time.Time) - - for market := range marketToIntervalMap { - mapLastSent[market] = now - } - - priceUpdater, err := ports.NewTdexDaemonPriceUpdater( - tdexOperatorEndpoint, macaroonsPath, tlsCertPath, - ) - if err != nil { - return nil, err - } - - tdexTarget := &TdexDaemonTarget{ - Endpoint: tdexOperatorEndpoint, - priceUpdater: priceUpdater, - priceUpdaterLocker: &sync.Mutex{}, - closeChan: make(chan bool, 1), - marketsToUpdate: make(map[domain.Market]domain.Price), - } - - for market, interval := range marketToIntervalMap { - go func(duration time.Duration, market domain.Market) { - for { - select { - case <-tdexTarget.closeChan: - log.Info("Stop the tdex updater") - break - case <-time.After(duration): - tdexTarget.updatePrice(market) - continue - } - } - }(interval, market) - } - - return tdexTarget, nil -} - -// 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() - - daemon.marketsToUpdate[marketPrice.Market] = marketPrice.Price -} - -// Stop is used to stop all the goroutines launched in NewTdexDaemonTarget -func (daemon *TdexDaemonTarget) Stop() { - daemon.closeChan <- true -} - -func (daemon *TdexDaemonTarget) updatePrice(market domain.Market) { - daemon.priceUpdaterLocker.Lock() - defer daemon.priceUpdaterLocker.Unlock() - - 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/config/config.go b/internal/config/config.go new file mode 100644 index 0000000..576f61e --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,49 @@ +package config + +import ( + "github.com/spf13/viper" +) + +const ( + ConfigKey = "CONFIG_PATH" + LogLevelKey = "LOG_LEVEL" +) + +var ( + defaultConfig = "." +) + +func init() { + viper.SetEnvPrefix("FEEDER") + viper.AutomaticEnv() + viper.SetDefault(ConfigKey, defaultConfig) + viper.SetDefault(LogLevelKey, 4) + + configPath := viper.GetString(ConfigKey) + if configPath != defaultConfig { + viper.SetConfigFile(configPath) + } else { + viper.SetConfigName("config") + viper.SetConfigType("json") + viper.AddConfigPath(configPath) + } +} + +func GetConfigPath() string { + configPath := viper.GetString(ConfigKey) + return configPath +} + +func NewConfigFromFile() (Config, error) { + if err := viper.ReadInConfig(); err != nil { + return Config{}, err + } + cfg := Config{} + if err := viper.Unmarshal(&cfg); err != nil { + return Config{}, err + } + if err := cfg.Validate(); err != nil { + return Config{}, err + } + return cfg, nil +} diff --git a/internal/config/types.go b/internal/config/types.go new file mode 100644 index 0000000..cad176b --- /dev/null +++ b/internal/config/types.go @@ -0,0 +1,114 @@ +package config + +import ( + "encoding/hex" + "fmt" + + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +type Target struct { + RPCAddress string `mapstructure:"rpc_address"` + TLSCertPath string `mapstructure:"tls_cert_path,omitempty"` + MacaroonsPath string `mapstructure:"macaroons_path,omitempty"` +} + +func (t Target) validate() error { + if t.RPCAddress == "" { + return fmt.Errorf("target rpc address must not be nil") + } + macPathOk := t.MacaroonsPath != "" + certPathOk := t.TLSCertPath != "" + if macPathOk != certPathOk { + return fmt.Errorf( + "macaroon and tls cert paths must be both either set or unset for a target", + ) + } + return nil +} + +type Market struct { + CBaseAsset string `mapstructure:"base_asset"` + CQuoteAsset string `mapstructure:"quote_asset"` + CTicker string `mapstructure:"ticker"` + CTargets []Target `mapstructure:"targets"` +} + +func (m Market) BaseAsset() string { + return m.CBaseAsset +} + +func (m Market) QuoteAsset() string { + return m.CQuoteAsset +} + +func (m Market) Ticker() string { + return m.CTicker +} + +func (m Market) validate() error { + if m.BaseAsset() == "" { + return fmt.Errorf("market base asset must not be nil") + } + ba, err := hex.DecodeString(m.BaseAsset()) + if err != nil { + return fmt.Errorf("market base asset must be an hex string") + } + if len(ba) != 32 { + return fmt.Errorf("market base asset must be a 64-chars hex string") + } + if m.QuoteAsset() == "" { + return fmt.Errorf("market quote asset must not be nil") + } + qa, err := hex.DecodeString(m.QuoteAsset()) + if err != nil { + return fmt.Errorf("market quote asset must be an hex string") + } + if len(qa) != 32 { + return fmt.Errorf("market quote asset must be a 64-chars hex string") + } + if m.Ticker() == "" { + return fmt.Errorf("market ticker must not be nil") + } + if len(m.CTargets) <= 0 { + return fmt.Errorf("market must have at least one target") + } + for _, t := range m.CTargets { + if err := t.validate(); err != nil { + return err + } + } + return nil +} + +type Config struct { + PriceFeeder string `mapstructure:"price_feeder"` + Interval int `mapstructure:"interval"` + Markets []Market `mapstructure:"markets"` +} + +func (c Config) Validate() error { + if c.PriceFeeder == "" { + return fmt.Errorf("price_feeder must not be nil") + } + if c.Interval <= 0 { + return fmt.Errorf("interval must be a positive value") + } + if len(c.Markets) <= 0 { + return fmt.Errorf("markets must not be empty") + } + for _, mkt := range c.Markets { + if err := mkt.validate(); err != nil { + return err + } + } + return nil +} + +func (c Config) PortableMarkets() []ports.Market { + markets := make([]ports.Market, 0, len(c.Markets)) + for _, mkt := range c.Markets { + markets = append(markets, mkt) + } + return markets +} diff --git a/internal/core/application/service.go b/internal/core/application/service.go new file mode 100644 index 0000000..1d3e351 --- /dev/null +++ b/internal/core/application/service.go @@ -0,0 +1,119 @@ +package application + +import ( + "fmt" + "sync" + + log "github.com/sirupsen/logrus" + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +type Service interface { + Start() error + Stop() +} + +type IndexedTargetsByMarket map[string]map[string]ports.TdexClient + +type service struct { + priceFeeder ports.PriceFeeder + targetsByAddress map[string]ports.TdexClient + targetsByMarket IndexedTargetsByMarket + + lock *sync.Mutex +} + +func NewService( + priceFeeder ports.PriceFeeder, targetsByMarket IndexedTargetsByMarket, +) Service { + targetsByAddress := make(map[string]ports.TdexClient) + for _, targets := range targetsByMarket { + for _, t := range targets { + targetsByAddress[t.RPCAddress()] = t + } + } + + return &service{ + priceFeeder: priceFeeder, + targetsByMarket: targetsByMarket, + targetsByAddress: targetsByAddress, + lock: &sync.Mutex{}, + } +} + +func (s *service) Start() error { + s.lock.Lock() + defer s.lock.Unlock() + + errCount := 0 + for _, t := range s.targetsByAddress { + ok, err := t.IsReady() + if err != nil { + log.WithError(err).Warnf("cannot connect to daemon %s", t.RPCAddress()) + errCount++ + continue + } + if !ok { + log.Warnf("daemon %s is not ready", t.RPCAddress()) + errCount++ + } + } + + if errCount == len(s.targetsByAddress) { + return fmt.Errorf( + "none of the provided targets are either reachable or ready to be fed " + + "with prices", + ) + } + + if errCount > 0 { + log.Warn( + "some targets are neither reachable or ready to be fed with prices. " + + "Make sure they are up and reachable to receive price feeds", + ) + } + + var err error + go func() { + if _err := s.priceFeeder.Start(); _err != nil { + err = _err + } + }() + + for priceFeed := range s.priceFeeder.FeedChan() { + mkt := priceFeed.GetMarket() + price := priceFeed.GetPrice() + marketKey := ports.MarketKey(mkt) + targets := s.targetsByMarket[marketKey] + log.Infof( + "received price feed: market %s, price %s/%s", + mkt.Ticker(), price.BasePrice(), price.QuotePrice(), + ) + + for _, target := range targets { + t := target + go func() { + if isReady, _ := t.IsReady(); isReady { + if err := t.UpdateMarketPrice(mkt, price); err != nil { + log.WithError(err).Warnf( + "error while updating target %s", t.RPCAddress(), + ) + return + } + log.Infof( + "updated market price (%s) for target %s", + mkt.Ticker(), t.RPCAddress(), + ) + } + }() + } + } + + return err +} + +func (s *service) Stop() { + log.Info("stopping price feeder...") + s.priceFeeder.Stop() + log.Info("done") +} diff --git a/internal/core/application/service_test.go b/internal/core/application/service_test.go new file mode 100644 index 0000000..a15e8ca --- /dev/null +++ b/internal/core/application/service_test.go @@ -0,0 +1,138 @@ +package application_test + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "math/big" + "testing" + "time" + + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "github.com/tdex-network/tdex-feeder/internal/core/application" + krakenfeeder "github.com/tdex-network/tdex-feeder/internal/core/infrastructure/feeder/kraken" + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +func TestService(t *testing.T) { + appSvc, err := newTestService() + require.NoError(t, err) + + go func() { + time.Sleep(10 * time.Second) + appSvc.Stop() + }() + + err = appSvc.Start() + require.NoError(t, err) +} + +func newTestService() (application.Service, error) { + interval := 2000 // 2s interval + tickers := []string{"XBT/USDT", "XBT/EUR"} + markets := mockedMarkets(tickers) + + priceFeeder, err := krakenfeeder.NewKrakenPriceFeeder(interval, markets) + if err != nil { + return nil, err + } + + targetsByMarket := make(application.IndexedTargetsByMarket) + for _, mkt := range markets { + mktKey := ports.MarketKey(mkt) + targets := make(map[string]ports.TdexClient) + daemons := mockedDaemons(2) + for _, d := range daemons { + targets[d.RPCAddress()] = d + } + targetsByMarket[mktKey] = targets + } + + return application.NewService(priceFeeder, targetsByMarket), nil +} + +func mockedMarkets(tickers []string) []ports.Market { + markets := make([]ports.Market, 0, len(tickers)) + for _, ticker := range tickers { + markets = append(markets, &mockMarket{ + baseAsset: randomHex(32), + quoteAsset: randomHex(32), + ticker: ticker, + }) + } + return markets +} + +func mockedDaemons(num int) []ports.TdexClient { + daemons := make([]ports.TdexClient, 0, num) + for i := 0; i < num; i++ { + mockedDaemon := &mockDaemon{} + mockedDaemon.On("RPCAddress").Return(randomAddr()) + mockedDaemon.On("IsReady").Return(true, nil) + mockedDaemon.On("UpdateMarketPrice", mock.Anything, mock.Anything).Return(nil) + daemons = append(daemons, mockedDaemon) + } + return daemons +} + +type mockMarket struct { + baseAsset string + quoteAsset string + ticker string +} + +func (m *mockMarket) BaseAsset() string { + return m.baseAsset +} + +func (m *mockMarket) QuoteAsset() string { + return m.quoteAsset +} + +func (m *mockMarket) Ticker() string { + return m.ticker +} + +type mockDaemon struct { + mock.Mock +} + +func (m *mockDaemon) RPCAddress() string { + args := m.Called() + return args.Get(0).(string) +} + +func (m mockDaemon) IsReady() (bool, error) { + args := m.Called() + + var res bool + if a := args.Get(0); a != nil { + res = a.(bool) + } + return res, args.Error(1) +} + +func (m mockDaemon) UpdateMarketPrice(mkt ports.Market, prc ports.Price) error { + args := m.Called(mkt, prc) + return args.Error(0) +} + +func randomAddr() string { + return fmt.Sprintf("localhost:%d", randomIntInRange(1024, 9999)) +} + +func randomHex(len int) string { + return hex.EncodeToString(randomBytes(len)) +} + +func randomBytes(len int) []byte { + b := make([]byte, len) + rand.Read(b) + return b +} + +func randomIntInRange(min, max int) int { + n, _ := rand.Int(rand.Reader, big.NewInt(int64(max))) + return int(int(n.Int64())) + min +} diff --git a/internal/core/infrastructure/client/grpc/service.go b/internal/core/infrastructure/client/grpc/service.go new file mode 100644 index 0000000..94a0072 --- /dev/null +++ b/internal/core/infrastructure/client/grpc/service.go @@ -0,0 +1,120 @@ +package grpcdaemon + +import ( + "context" + "fmt" + "io/ioutil" + "strconv" + + "github.com/tdex-network/tdex-daemon/pkg/macaroons" + "github.com/tdex-network/tdex-feeder/internal/core/ports" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "gopkg.in/macaroon.v2" + + pboperator "github.com/tdex-network/tdex-daemon/api-spec/protobuf/gen/operator" + pbunlocker "github.com/tdex-network/tdex-daemon/api-spec/protobuf/gen/walletunlocker" + pbtypes "github.com/tdex-network/tdex-protobuf/generated/go/types" +) + +var ( + maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200) +) + +type service struct { + rpcAddress string + + unlockerClient pbunlocker.WalletUnlockerClient + operatorClient pboperator.OperatorClient +} + +func NewGRPCClient( + addr, macaroonsPath, tlsCertPath string, +) (ports.TdexClient, error) { + unlockerConn, err := createGRPCConn(addr, "", "") + if err != nil { + return nil, err + } + + operatorConn, err := createGRPCConn(addr, macaroonsPath, tlsCertPath) + if err != nil { + return nil, err + } + + unlockerClient := pbunlocker.NewWalletUnlockerClient(unlockerConn) + operatorClient := pboperator.NewOperatorClient(operatorConn) + + return &service{ + rpcAddress: addr, + unlockerClient: unlockerClient, + operatorClient: operatorClient, + }, nil +} + +func (s *service) RPCAddress() string { + return s.rpcAddress +} + +func (s *service) IsReady() (bool, error) { + res, err := s.unlockerClient.IsReady( + context.Background(), &pbunlocker.IsReadyRequest{}, + ) + if err != nil { + return false, err + } + return res.GetInitialized() && res.GetUnlocked(), nil +} + +func (s *service) UpdateMarketPrice( + mkt ports.Market, price ports.Price, +) error { + basePrice, _ := strconv.ParseFloat(price.BasePrice(), 32) + quotePrice, _ := strconv.ParseFloat(price.QuotePrice(), 32) + _, err := s.operatorClient.UpdateMarketPrice( + context.Background(), &pboperator.UpdateMarketPriceRequest{ + Market: &pbtypes.Market{ + BaseAsset: mkt.BaseAsset(), + QuoteAsset: mkt.QuoteAsset(), + }, + Price: &pbtypes.Price{ + BasePrice: float32(basePrice), + QuotePrice: float32(quotePrice), + }, + }, + ) + return err +} + +func createGRPCConn(daemonEndpoint, macPath, certPath string) (*grpc.ClientConn, error) { + opts := []grpc.DialOption{grpc.WithDefaultCallOptions(maxMsgRecvSize)} + + if len(macPath) <= 0 { + opts = append(opts, grpc.WithInsecure()) + } else { + // TLS credentials + tlsCreds, err := credentials.NewClientTLSFromFile(certPath, "") + if err != nil { + return nil, fmt.Errorf("could not read TLS certificate: %s", err) + } + // macaroons credentials + macBytes, err := ioutil.ReadFile(macPath) + if err != nil { + return nil, fmt.Errorf("could not read macaroon %s: %s", macPath, err) + } + mac := &macaroon.Macaroon{} + if err = mac.UnmarshalBinary(macBytes); err != nil { + return nil, fmt.Errorf("could not parse macaroon %s: %s", macPath, err) + } + macCreds := macaroons.NewMacaroonCredential(mac, true) + + opts = append(opts, grpc.WithPerRPCCredentials(macCreds)) + opts = append(opts, grpc.WithTransportCredentials(tlsCreds)) + } + + conn, err := grpc.Dial(daemonEndpoint, opts...) + if err != nil { + return nil, fmt.Errorf("unable to connect to RPC server: %v", err) + } + + return conn, nil +} diff --git a/internal/core/infrastructure/feeder/kraken/service.go b/internal/core/infrastructure/feeder/kraken/service.go new file mode 100644 index 0000000..8a4354d --- /dev/null +++ b/internal/core/infrastructure/feeder/kraken/service.go @@ -0,0 +1,263 @@ +package krakenfeeder + +import ( + "encoding/json" + "fmt" + "sync" + "time" + + "github.com/gorilla/websocket" + "github.com/shopspring/decimal" + log "github.com/sirupsen/logrus" + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +const ( + // KrakenWebSocketURL is the base url to open a connection with kraken. + // This can be tweaked if in the future it might change, even if unlikely. + KrakenWebSocketURL = "ws.kraken.com" +) + +type service struct { + conn *websocket.Conn + writeTicker *time.Ticker + lock *sync.RWMutex + chLock *sync.Mutex + + marketByTicker map[string]ports.Market + latestFeedsByTicker map[string]ports.PriceFeed + feedChan chan ports.PriceFeed + quitChan chan struct{} +} + +func NewKrakenPriceFeeder(args ...interface{}) (ports.PriceFeeder, error) { + if len(args) != 2 { + return nil, fmt.Errorf("invalid number of args") + } + + interval, ok := args[0].(int) + if !ok { + return nil, fmt.Errorf("unknown interval arg type") + } + + markets, ok := args[1].([]ports.Market) + if !ok { + return nil, fmt.Errorf("unknown marktes arg type") + } + + writeTicker := time.NewTicker(time.Duration(interval) * time.Millisecond) + mktTickers := make([]string, 0, len(markets)) + mktByTicker := make(map[string]ports.Market) + for _, mkt := range markets { + mktTickers = append(mktTickers, mkt.Ticker()) + mktByTicker[mkt.Ticker()] = mkt + } + + conn, err := connectAndSubscribe(mktTickers) + if err != nil { + return nil, err + } + conn.CloseHandler() + + return &service{ + conn: conn, + writeTicker: writeTicker, + lock: &sync.RWMutex{}, + chLock: &sync.Mutex{}, + marketByTicker: mktByTicker, + latestFeedsByTicker: make(map[string]ports.PriceFeed), + feedChan: make(chan ports.PriceFeed), + quitChan: make(chan struct{}, 1), + }, nil +} + +func (s *service) Start() error { + mustReconnect, err := s.start() + for mustReconnect { + log.WithError(err).Warn("connection dropped unexpectedly. Trying to reconnect...") + + tickers := make([]string, 0, len(s.marketByTicker)) + for ticker := range s.marketByTicker { + tickers = append(tickers, ticker) + } + + var conn *websocket.Conn + conn, err = connectAndSubscribe(tickers) + if err != nil { + return err + } + s.conn = conn + + log.Debug("connection and subscriptions re-established. Restarting...") + mustReconnect, err = s.start() + } + + return err +} + +func (s *service) Stop() { + s.quitChan <- struct{}{} +} + +func (s *service) FeedChan() chan ports.PriceFeed { + return s.feedChan +} + +func (s *service) start() (mustReconnect bool, err error) { + defer func() { + if rec := recover(); rec != nil { + mustReconnect = true + } + }() + + go func() { + for range s.writeTicker.C { + s.writeToFeedChan() + } + }() + + for { + select { + case <-s.quitChan: + s.writeTicker.Stop() + s.closeChannels() + err = s.conn.Close() + return false, err + default: + // Referred to: + // + // https://support.kraken.com/hc/en-us/articles/360044504011-WebSocket-API-unexpected-disconnections-from-market-data-feeds + // + // Sometimes it can happen that the line below panics instead of + // returning an UnexpectedCloseError. Because of this it's + // mandatory here to recover a potential panic to signal that the + // connection must be re-established. + // Even in case the line below returns an UnexpectedCloseError, + // this is used to panic so the deferred recover function is reused + // to still signal the need for a reconnection with kraken websocket. + _, message, err := s.conn.ReadMessage() + if err != nil { + if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { + panic(err) + } + } + + priceFeed := s.parseFeed(message) + if priceFeed == nil { + continue + } + + s.writePriceFeed(priceFeed.GetMarket().Ticker(), priceFeed) + } + } +} + +func (s *service) readPriceFeeds() []ports.PriceFeed { + s.lock.RLock() + defer s.lock.RUnlock() + + feeds := make([]ports.PriceFeed, 0, len(s.latestFeedsByTicker)) + for _, priceFeed := range s.latestFeedsByTicker { + feeds = append(feeds, priceFeed) + } + return feeds +} + +func (s *service) writePriceFeed(mktTicker string, priceFeed ports.PriceFeed) { + s.lock.Lock() + defer s.lock.Unlock() + s.latestFeedsByTicker[mktTicker] = priceFeed +} + +func (s *service) writeToFeedChan() { + s.chLock.Lock() + defer s.chLock.Unlock() + + priceFeeds := s.readPriceFeeds() + for _, priceFeed := range priceFeeds { + s.feedChan <- priceFeed + } +} + +func (s *service) closeChannels() { + s.chLock.Lock() + defer s.chLock.Unlock() + + close(s.feedChan) + close(s.quitChan) +} + +func (s *service) parseFeed(msg []byte) ports.PriceFeed { + var i []interface{} + if err := json.Unmarshal(msg, &i); err != nil { + return nil + } + if len(i) != 4 { + return nil + } + + ticker, ok := i[3].(string) + if !ok { + return nil + } + + mkt, ok := s.marketByTicker[ticker] + if !ok { + return nil + } + + ii, ok := i[1].(map[string]interface{}) + if !ok { + return nil + } + + iii, ok := ii["c"].([]interface{}) + if !ok { + return nil + } + + if len(iii) < 1 { + return nil + } + priceStr, ok := iii[0].(string) + if !ok { + return nil + } + + quotePrice, err := decimal.NewFromString(priceStr) + if err != nil { + return nil + } + basePrice := decimal.NewFromInt(1).Div(quotePrice) + + return &priceFeed{ + market: mkt, + price: &price{ + basePrice: basePrice.StringFixed(8), + quotePrice: quotePrice.String(), + }, + } +} + +func connectAndSubscribe(mktTickers []string) (*websocket.Conn, error) { + url := fmt.Sprintf("wss://%s", KrakenWebSocketURL) + conn, _, err := websocket.DefaultDialer.Dial(url, nil) + if err != nil { + return nil, err + } + + msg := map[string]interface{}{ + "event": "subscribe", + "pair": mktTickers, + "subscription": map[string]string{ + "name": "ticker", + }, + } + + buf, _ := json.Marshal(msg) + if err := conn.WriteMessage(websocket.TextMessage, buf); err != nil { + return nil, fmt.Errorf("cannot subscribe to given markets: %s", err) + } + + return conn, nil +} diff --git a/internal/core/infrastructure/feeder/kraken/service_test.go b/internal/core/infrastructure/feeder/kraken/service_test.go new file mode 100644 index 0000000..6001577 --- /dev/null +++ b/internal/core/infrastructure/feeder/kraken/service_test.go @@ -0,0 +1,94 @@ +package krakenfeeder_test + +import ( + "crypto/rand" + "encoding/hex" + "testing" + "time" + + "github.com/stretchr/testify/require" + krakenfeeder "github.com/tdex-network/tdex-feeder/internal/core/infrastructure/feeder/kraken" + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +var ( + interval = 1000 // 1s interval + tickers = []string{"XBT/USDT", "XBT/EUR"} +) + +func TestService(t *testing.T) { + feederSvc, err := newTestService() + require.NoError(t, err) + + go func() { + err := feederSvc.Start() + require.NoError(t, err) + }() + + go func() { + time.Sleep(5 * time.Second) + feederSvc.Stop() + }() + + count := 0 + for priceFeed := range feederSvc.FeedChan() { + count++ + require.NotNil(t, priceFeed.GetMarket()) + require.NotNil(t, priceFeed.GetPrice()) + require.NotEmpty(t, priceFeed.GetMarket().BaseAsset()) + require.NotEmpty(t, priceFeed.GetMarket().QuoteAsset()) + require.NotEmpty(t, priceFeed.GetMarket().Ticker()) + require.NotEmpty(t, priceFeed.GetPrice().BasePrice()) + require.NotEmpty(t, priceFeed.GetPrice().QuotePrice()) + } + require.Greater(t, count, 0) +} + +func newTestService() (ports.PriceFeeder, error) { + markets := mockedMarkets(tickers) + return krakenfeeder.NewKrakenPriceFeeder(interval, markets) +} + +func mockedMarkets(tickers []string) []ports.Market { + markets := make([]ports.Market, 0, len(tickers)) + for _, ticker := range tickers { + markets = append(markets, newMockedMarket(ticker)) + } + return markets +} + +type mockMarket struct { + baseAsset string + quoteAsset string + ticker string +} + +func newMockedMarket(ticker string) ports.Market { + return &mockMarket{ + baseAsset: randomHex(32), + quoteAsset: randomHex(32), + ticker: ticker, + } +} + +func (m *mockMarket) BaseAsset() string { + return m.baseAsset +} + +func (m *mockMarket) QuoteAsset() string { + return m.quoteAsset +} + +func (m *mockMarket) Ticker() string { + return m.ticker +} + +func randomHex(len int) string { + return hex.EncodeToString(randomBytes(len)) +} + +func randomBytes(len int) []byte { + b := make([]byte, len) + rand.Read(b) + return b +} diff --git a/internal/core/infrastructure/feeder/kraken/types.go b/internal/core/infrastructure/feeder/kraken/types.go new file mode 100644 index 0000000..080d851 --- /dev/null +++ b/internal/core/infrastructure/feeder/kraken/types.go @@ -0,0 +1,31 @@ +package krakenfeeder + +import ( + "github.com/tdex-network/tdex-feeder/internal/core/ports" +) + +type price struct { + basePrice string + quotePrice string +} + +func (p *price) BasePrice() string { + return p.basePrice +} + +func (p *price) QuotePrice() string { + return p.quotePrice +} + +type priceFeed struct { + market ports.Market + price *price +} + +func (p *priceFeed) GetMarket() ports.Market { + return p.market +} + +func (p *priceFeed) GetPrice() ports.Price { + return p.price +} diff --git a/internal/core/ports/interfaces.go b/internal/core/ports/interfaces.go new file mode 100644 index 0000000..0a90212 --- /dev/null +++ b/internal/core/ports/interfaces.go @@ -0,0 +1,15 @@ +package ports + +type PriceFeeder interface { + Start() error + Stop() + + FeedChan() chan PriceFeed +} + +type TdexClient interface { + RPCAddress() string + + IsReady() (bool, error) + UpdateMarketPrice(market Market, price Price) error +} diff --git a/internal/core/ports/types.go b/internal/core/ports/types.go new file mode 100644 index 0000000..5e95dc6 --- /dev/null +++ b/internal/core/ports/types.go @@ -0,0 +1,28 @@ +package ports + +import ( + "crypto/sha256" + "encoding/hex" +) + +type Market interface { + BaseAsset() string + QuoteAsset() string + Ticker() string +} + +type Price interface { + BasePrice() string + QuotePrice() string +} + +type PriceFeed interface { + GetMarket() Market + GetPrice() Price +} + +func MarketKey(mkt Market) string { + key := mkt.BaseAsset() + mkt.QuoteAsset() + keyBytes := sha256.Sum256([]byte(key)) + return hex.EncodeToString(keyBytes[:]) +} diff --git a/internal/domain/feed.go b/internal/domain/feed.go deleted file mode 100644 index de8cdf4..0000000 --- a/internal/domain/feed.go +++ /dev/null @@ -1,33 +0,0 @@ -package domain - -// MarketPrice represents a new price associated with a given market -type MarketPrice struct { - Market Market - Price Price -} - -// Feed represents a source of MarketPrice data -type Feed interface { - AddMarketPrice(marketPrice MarketPrice) - GetMarketPriceChan() <-chan MarketPrice -} - -type feed struct { - marketPriceChan chan MarketPrice -} - -// NewFeed creates a Feed (i.e an empty channel) -func NewFeed() Feed { - return &feed{ - marketPriceChan: make(chan MarketPrice), - } -} - -// AddMarketPrice send a new marketPrice value inside the Feed's channel. -func (f feed) AddMarketPrice(marketPrice MarketPrice) { - f.marketPriceChan <- marketPrice -} - -func (f feed) GetMarketPriceChan() <-chan MarketPrice { - return f.marketPriceChan -} diff --git a/internal/domain/market.go b/internal/domain/market.go deleted file mode 100644 index 2de7ff1..0000000 --- a/internal/domain/market.go +++ /dev/null @@ -1,7 +0,0 @@ -package domain - -// Market is represented by his base asset & his quote asset -type Market struct { - BaseAsset string - QuoteAsset string -} diff --git a/internal/domain/price.go b/internal/domain/price.go deleted file mode 100644 index 78d7166..0000000 --- a/internal/domain/price.go +++ /dev/null @@ -1,7 +0,0 @@ -package domain - -// Price represents a price for a market -type Price struct { - BasePrice float32 - QuotePrice float32 -} diff --git a/internal/domain/target.go b/internal/domain/target.go deleted file mode 100644 index 0b022b4..0000000 --- a/internal/domain/target.go +++ /dev/null @@ -1,6 +0,0 @@ -package domain - -// Target interface is used to push new prices fetched from feeds -type Target interface { - Push(marketPrice MarketPrice) -} diff --git a/internal/ports/kraken_websocket.go b/internal/ports/kraken_websocket.go deleted file mode 100644 index 6f937c8..0000000 --- a/internal/ports/kraken_websocket.go +++ /dev/null @@ -1,85 +0,0 @@ -package ports - -import ( - "net/url" - - "github.com/gorilla/websocket" - log "github.com/sirupsen/logrus" -) - -// KrakenWebSocket is the interface to manage kraken web socket streams -type KrakenWebSocket interface { - Connect() error - Start() (chan TickerWithPrice, error) - Stop() error -} - -type krakenWebSocket struct { - krakenWebSocketConn *websocket.Conn - tickerWithPriceChan chan TickerWithPrice - tickersToSubscribe []string - quitChan chan bool -} - -// NewKrakenWebSocket is a factory function for KrakenWebSocket interface -func NewKrakenWebSocket(tickersToSubscribe []string) KrakenWebSocket { - return &krakenWebSocket{ - krakenWebSocketConn: nil, - tickerWithPriceChan: make(chan TickerWithPrice), - tickersToSubscribe: tickersToSubscribe, - quitChan: make(chan bool, 1), - } -} - -// Connect method will connect to the websocket kraken server, ping it and subscribe to tickers threads. -func (socket *krakenWebSocket) Connect() error { - // connect to server - url := url.URL{Scheme: "wss", Host: krakenWebSocketURL, Path: "/"} - websocketConn, _, err := websocket.DefaultDialer.Dial(url.String(), nil) - if err != nil { - return err - } - - socket.krakenWebSocketConn = websocketConn - subscribeMsg := createSubscribeToMarketMessage(socket.tickersToSubscribe) - return sendRequestMessage(socket.krakenWebSocketConn, subscribeMsg) -} - -func (socket *krakenWebSocket) Start() (chan TickerWithPrice, error) { - go socket.listen() - return socket.tickerWithPriceChan, nil -} - -func (socket *krakenWebSocket) listen() { - for { - select { - case <-socket.quitChan: - return - default: - _, message, err := socket.krakenWebSocketConn.ReadMessage() - if err != nil { - if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) { - log.Warn("error: ", err) - } - break - } - - tickerWithPrice, err := toTickerWithPrice(message) - if err != nil { - continue - } - - socket.tickerWithPriceChan <- *tickerWithPrice - } - } -} - -func (socket *krakenWebSocket) Stop() error { - socket.quitChan <- true - err := socket.krakenWebSocketConn.Close() - if err != nil { - return err - } - close(socket.tickerWithPriceChan) - return nil -} diff --git a/internal/ports/kraken_websocket_test.go b/internal/ports/kraken_websocket_test.go deleted file mode 100644 index 8339c41..0000000 --- a/internal/ports/kraken_websocket_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package ports - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -const ( - ticker = "XBT/USDT" -) - -func createAndConnect() (KrakenWebSocket, error) { - krakenWS := NewKrakenWebSocket([]string{ticker}) - err := krakenWS.Connect() - return krakenWS, err -} - -func TestConnectToKrakenWebSocket(t *testing.T) { - _, err := createAndConnect() - assert.Nil(t, err) -} - -func TestStartStop(t *testing.T) { - ws, err := createAndConnect() - if err != nil { - t.Error(err) - } - - tickerWithPriceChan, err := ws.Start() - assert.NoError(t, err) - - go func() { - for nextTickerWithPrice := range tickerWithPriceChan { - assert.NotNil(t, nextTickerWithPrice) - } - }() - - err = ws.Stop() - assert.NoError(t, err) -} diff --git a/internal/ports/socket.go b/internal/ports/socket.go deleted file mode 100644 index cc6036c..0000000 --- a/internal/ports/socket.go +++ /dev/null @@ -1,98 +0,0 @@ -package ports - -import ( - "encoding/json" - "errors" - "strconv" - - "github.com/gorilla/websocket" -) - -const ( - krakenWebSocketURL = "ws.kraken.com" -) - -var ( - NotSubscribeError = errors.New("message is not a subscribe response") -) - -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"` -} - -// 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(marketpairs []string) RequestMessage { - s := Subscription{Name: "ticker"} - return RequestMessage{"subscribe", marketpairs, &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 -} - -func toTickerWithPrice(message []byte) (*TickerWithPrice, error) { - var result []interface{} - err := json.Unmarshal([]byte(message), &result) - if err != nil { - return nil, err - } - - if len(result) == 4 { - pair, ok := result[3].(string) - if !ok { - return nil, NotSubscribeError - } - - temp, ok := result[1].(map[string]interface{}) - if !ok { - return nil, NotSubscribeError - } - - prices, ok := temp["c"].([]interface{}) - if !ok { - return nil, NotSubscribeError - } - - price, err := strconv.ParseFloat(prices[0].(string), 32) - if err != nil { - return nil, err - } - - return &TickerWithPrice{ - Ticker: pair, - Price: price, - }, nil - } - - return nil, NotSubscribeError -} diff --git a/internal/ports/tdex_daemon_price_updater.go b/internal/ports/tdex_daemon_price_updater.go deleted file mode 100644 index 105c3d6..0000000 --- a/internal/ports/tdex_daemon_price_updater.go +++ /dev/null @@ -1,113 +0,0 @@ -package ports - -import ( - "context" - "errors" - "fmt" - "io/ioutil" - - log "github.com/sirupsen/logrus" - "gopkg.in/macaroon.v2" - - "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" - "google.golang.org/grpc/credentials" -) - -var ( - maxMsgRecvSize = grpc.MaxCallRecvMsgSize(1 * 1024 * 1024 * 200) -) - -// TdexDaemonPriceUpdater is a grpc client using to call UpdateMarketPrice RPC of tdex daemon -type TdexDaemonPriceUpdater interface { - UpdateMarketPrice(ctx context.Context, marketPrice domain.MarketPrice) error -} - -// NewTdexDaemonPriceUpdater uses the operatorInterfaceEndpoint to create a gRPC client. -func NewTdexDaemonPriceUpdater( - operatorInterfaceEndpoint, macaroonsPath, tlsCertPath string, -) (TdexDaemonPriceUpdater, error) { - if macOK, certOK := macaroonsPath != "", tlsCertPath != ""; macOK != certOK { - return nil, fmt.Errorf("both macaroons filepath and TLS cert path must be defined") - } - - connGrpc, err := connectToGRPC(operatorInterfaceEndpoint, macaroonsPath, tlsCertPath) - if err != nil { - return nil, err - } - - // TOSO: Add health check, - return &tdexDaemonPriceUpdater{ - clientGRPC: pboperator.NewOperatorClient(connGrpc), - }, nil -} - -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") - } - - 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.QuotePrice, - }, - } - - _, 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(daemonEndpoint, macPath, certPath string) (*grpc.ClientConn, error) { - opts := []grpc.DialOption{grpc.WithDefaultCallOptions(maxMsgRecvSize)} - - if len(macPath) <= 0 { - opts = append(opts, grpc.WithInsecure()) - } else { - tlsCreds, err := credentials.NewClientTLSFromFile(certPath, "") - if err != nil { - return nil, fmt.Errorf("could not read TLS certificate: %s", err) - } - - macBytes, err := ioutil.ReadFile(macPath) - if err != nil { - return nil, fmt.Errorf("could not read macaroon %s: %s", macPath, err) - } - mac := &macaroon.Macaroon{} - err = mac.UnmarshalBinary(macBytes) - if err != nil { - return nil, fmt.Errorf("could not parse macaroon %s: %s", macPath, err) - } - macCreds := NewMacaroonCredential(mac) - opts = append(opts, grpc.WithPerRPCCredentials(macCreds)) - opts = append(opts, grpc.WithTransportCredentials(tlsCreds)) - } - - conn, err := grpc.Dial(daemonEndpoint, opts...) - if err != nil { - return nil, fmt.Errorf("unable to connect to RPC server: %v", err) - } - - log.Println("Connected to gRPC:", daemonEndpoint) - return conn, nil -} diff --git a/internal/ports/types.go b/internal/ports/types.go deleted file mode 100644 index ed535cf..0000000 --- a/internal/ports/types.go +++ /dev/null @@ -1,51 +0,0 @@ -package ports - -import ( - "context" - "encoding/hex" - - "gopkg.in/macaroon.v2" -) - -// TickerWithPrice is a struct using to represent ticker to subscribe in kraken web socket feed -type TickerWithPrice struct { - Ticker string - Price float64 -} - -// MacaroonCredential wraps a macaroon to implement the -// credentials.PerRPCCredentials interface. -type MacaroonCredential struct { - *macaroon.Macaroon -} - -// RequireTransportSecurity implements the PerRPCCredentials interface. -func (m MacaroonCredential) RequireTransportSecurity() bool { - return true -} - -// GetRequestMetadata implements the PerRPCCredentials interface. This method -// is required in order to pass the wrapped macaroon into the gRPC context. -// With this, the macaroon will be available within the request handling scope -// of the ultimate gRPC server implementation. -func (m MacaroonCredential) GetRequestMetadata( - ctx context.Context, uri ...string, -) (map[string]string, error) { - - macBytes, err := m.MarshalBinary() - if err != nil { - return nil, err - } - - md := make(map[string]string) - md["macaroon"] = hex.EncodeToString(macBytes) - return md, nil -} - -// NewMacaroonCredential returns a copy of the passed macaroon wrapped in a -// MacaroonCredential struct which implements PerRPCCredentials. -func NewMacaroonCredential(m *macaroon.Macaroon) MacaroonCredential { - ms := MacaroonCredential{} - ms.Macaroon = m.Clone() - return ms -} diff --git a/pkg/feeder/feeder.go b/pkg/feeder/feeder.go deleted file mode 100644 index d799eb7..0000000 --- a/pkg/feeder/feeder.go +++ /dev/null @@ -1,96 +0,0 @@ -package feeder - -import ( - "errors" - "sync" - - log "github.com/sirupsen/logrus" - "github.com/tdex-network/tdex-feeder/internal/domain" -) - -// Service is the interface describing the feeder behavior -type Service interface { - Start() error - Stop() - IsRunning() bool -} - -type feederService struct { - feeds []domain.Feed - targets []domain.Target - stopChan chan bool - running bool - locker sync.Locker -} - -// NewFeeder is the factory function for feeder service -func NewFeeder(feeds []domain.Feed, targets []domain.Target) Service { - return &feederService{ - feeds: feeds, - targets: targets, - stopChan: make(chan bool), - running: false, - locker: &sync.Mutex{}, - } -} - -// Start observe all the feeds chan (using merge function) -// and push the results to all targets -func (t *feederService) Start() error { - if t.IsRunning() { - return errors.New("the feeder is already started") - } - - t.running = true - marketPriceChannel := merge(t.feeds...) - - for t.IsRunning() { - select { - case <-t.stopChan: - t.running = false - break - case marketPrice := <-marketPriceChannel: - 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) - } - } - } - - return nil -} - -func (t *feederService) Stop() { - t.stopChan <- true -} - -func (t *feederService) IsRunning() bool { - t.locker.Lock() - 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/pkg/feeder/feeder_test.go b/pkg/feeder/feeder_test.go deleted file mode 100644 index 045a4e6..0000000 --- a/pkg/feeder/feeder_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package feeder - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/tdex-network/tdex-feeder/internal/domain" - "github.com/tdex-network/tdex-feeder/pkg/testutils" -) - -func TestFeeder(t *testing.T) { - feed := domain.NewFeed() - feedBis := domain.NewFeed() - - target := &testutils.MockTarget{ - MarketPrices: make([]domain.MarketPrice, 0), - } - - feeder := NewFeeder( - []domain.Feed{feed, feedBis}, - []domain.Target{target}, - ) - - marketPrice := domain.MarketPrice{ - Market: domain.Market{ - BaseAsset: "1111", - QuoteAsset: "0000", - }, - Price: domain.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) - feeder.Stop() - - assert.Equal(t, 15, len(target.MarketPrices)) -} diff --git a/pkg/testutils/mock_target.go b/pkg/testutils/mock_target.go deleted file mode 100644 index 00145f0..0000000 --- a/pkg/testutils/mock_target.go +++ /dev/null @@ -1,13 +0,0 @@ -package testutils - -import "github.com/tdex-network/tdex-feeder/internal/domain" - -// MockTarget simulates a target interface -type MockTarget struct { - MarketPrices []domain.MarketPrice -} - -// Push is the target method -func (t *MockTarget) Push(marketPrice domain.MarketPrice) { - t.MarketPrices = append(t.MarketPrices, marketPrice) -}