diff options
-rw-r--r-- | arbiter/Dockerfile | 20 | ||||
-rw-r--r-- | arbiter/arbiter.go | 119 | ||||
-rw-r--r-- | arbiter/go.mod | 17 | ||||
-rw-r--r-- | arbiter/go.sum | 30 | ||||
-rw-r--r-- | hived/hived.go | 42 |
5 files changed, 228 insertions, 0 deletions
diff --git a/arbiter/Dockerfile b/arbiter/Dockerfile new file mode 100644 index 0000000..fe3fb31 --- /dev/null +++ b/arbiter/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.13 as builder +ENV GOPROXY=https://goproxy.io +RUN apk update && apk upgrade +RUN apk add go git +ENV GOPROXY=https://goproxy.io +COPY go.* /arbiter/ +RUN cd /arbiter && go mod download +COPY *.go /arbiter/ +RUN cd /arbiter && go build + +FROM alpine:3.15 as certbuilder +RUN apk add openssl +WORKDIR /certs +RUN openssl req -nodes -new -x509 -subj="/C=US/ST=Denial/L=springfield/O=Dis/CN=localhost" -keyout server.key -out server.cert + +# FROM gcr.io/distroless/static-debian10 +FROM alpine:3.13 +COPY --from=certbuilder /certs /certs +COPY --from=builder /arbiter/arbiter /arbiter/ +ENTRYPOINT ["/arbiter/arbiter"] diff --git a/arbiter/arbiter.go b/arbiter/arbiter.go new file mode 100644 index 0000000..2806842 --- /dev/null +++ b/arbiter/arbiter.go @@ -0,0 +1,119 @@ +package main + +import ( + "context" + "crypto/tls" + "errors" + "flag" + "fmt" + "net/http" + "os" + "os/signal" + "time" + + "github.com/go-redis/redis/v8" + "github.com/gorilla/mux" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +var ( + flagPort = flag.String("port", "8009", "determines the port the server will listen on") + flagInterval = flag.Float64("interval", 10, "In seconds, the delay between checking prices") + redisDB = flag.Int64("redisdb", 1, "determines the db number") + rdb *redis.Client + redisAddress = flag.String("redisaddress", "redis:6379", "determines the address of the redis instance") + redisPassword = flag.String("redispassword", "", "determines the password of the redis db") +) + +const ( + SERVER_DEPLOYMENT_TYPE = "SERVER_DEPLOYMENT_TYPE" +) + +// OWASP: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html +func addSecureHeaders(w *http.ResponseWriter) { + (*w).Header().Set("Cache-Control", "no-store") + (*w).Header().Set("Content-Security-Policy", "default-src https;") + (*w).Header().Set("Strict-Transport-Security", "max-age=63072000;") + (*w).Header().Set("X-Content-Type-Options", "nosniff") + (*w).Header().Set("X-Frame-Options", "DENY") + (*w).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") +} + +func arbHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", "application/json") + if r.Method != "GET" { + http.Error(w, "Method is not supported.", http.StatusNotFound) + } + addSecureHeaders(&w) + //binance + //kucoin + +} + +func startServer(gracefulWait time.Duration) { + r := mux.NewRouter() + cfg := &tls.Config{ + MinVersion: tls.VersionTLS13, + // CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256}, + // PreferServerCipherSuites: true, + // CipherSuites: []uint16{ + // tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + // tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + // tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + // tls.TLS_RSA_WITH_AES_256_CBC_SHA, + // }, + } + srv := &http.Server{ + Addr: "0.0.0.0:" + *flagPort, + WriteTimeout: time.Second * 15, + ReadTimeout: time.Second * 15, + Handler: r, + TLSConfig: cfg, + } + r.HandleFunc("/crypto/arb", arbHandler) + + go func() { + var certPath, keyPath string + if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "deployment" { + certPath = "/certs/fullchain1.pem" + keyPath = "/certs/privkey1.pem" + } else if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "test" { + certPath = "/certs/server.cert" + keyPath = "/certs/server.key" + } else { + log.Fatal().Err(errors.New(fmt.Sprintf("unknown deployment kind: %s", SERVER_DEPLOYMENT_TYPE))) + } + if err := srv.ListenAndServeTLS(certPath, keyPath); err != nil { + log.Fatal().Err(err) + } + }() + + c := make(chan os.Signal, 1) + + signal.Notify(c, os.Interrupt) + <-c + ctx, cancel := context.WithTimeout(context.Background(), gracefulWait) + defer cancel() + srv.Shutdown(ctx) + log.Info().Msg("gracefully shut down the server") +} + +func setupLogging() { + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix +} + +func main() { + var gracefulWait time.Duration + flag.DurationVar(&gracefulWait, "gracefulwait", time.Second*15, "the duration to wait during the graceful shutdown") + flag.Parse() + rdb = redis.NewClient(&redis.Options{ + Addr: *redisAddress, + Password: *redisPassword, + DB: int(*redisDB), + }) + defer rdb.Close() + + setupLogging() + startServer(gracefulWait) +} diff --git a/arbiter/go.mod b/arbiter/go.mod new file mode 100644 index 0000000..57b26ac --- /dev/null +++ b/arbiter/go.mod @@ -0,0 +1,17 @@ +module arbiter + +go 1.18 + +require ( + github.com/go-redis/redis/v8 v8.11.5 + github.com/gorilla/mux v1.8.0 + github.com/rs/zerolog v1.27.0 +) + +require ( + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect +) diff --git a/arbiter/go.sum b/arbiter/go.sum new file mode 100644 index 0000000..d93030f --- /dev/null +++ b/arbiter/go.sum @@ -0,0 +1,30 @@ +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs= +github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/hived/hived.go b/hived/hived.go index 173e6c8..940c2ed 100644 --- a/hived/hived.go +++ b/hived/hived.go @@ -41,6 +41,7 @@ var ( const ( cryptocomparePriceURL = "https://min-api.cryptocompare.com/data/price?" + coingeckoAPIURLv3 = "https://api.coingecko.com/api/v3" changellyURL = "https://api.changelly.com" TELEGRAM_BOT_TOKEN_ENV_VAR = "TELEGRAM_BOT_TOKEN" CHANGELLY_API_KEY_ENV_VAR = "CHANGELLY_API_KEY" @@ -88,6 +89,47 @@ type errorChanStruct struct { err error } +func getPriceFromCoinGecko( + name, unit string, + wg *sync.WaitGroup, + priceChan chan<- priceChanStruct, + errChan chan<- errorChanStruct) { + defer wg.Done() + + params := "/simple/price?fsym=" + url.QueryEscape(name) + "&" + + "tsyms=" + url.QueryEscape(unit) + path := coingeckoAPIURLv3 + params + resp, err := http.Get(path) + if err != nil { + priceChan <- priceChanStruct{name: name, price: 0.} + errChan <- errorChanStruct{hasError: true, err: err} + log.Error().Err(err) + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + priceChan <- priceChanStruct{name: name, price: 0.} + errChan <- errorChanStruct{hasError: true, err: err} + log.Error().Err(err) + } + + jsonBody := make(map[string]interface{}) + err = json.Unmarshal(body, &jsonBody) + if err != nil { + priceChan <- priceChanStruct{name: name, price: 0.} + errChan <- errorChanStruct{hasError: true, err: err} + log.Error().Err(err) + } + + price := jsonBody[name].(map[string]interface{})[unit].(float64) + + log.Info().Msg(string(body)) + + priceChan <- priceChanStruct{name: name, price: price} + errChan <- errorChanStruct{hasError: false, err: nil} +} + func sendGetToCryptoCompare( name, unit string, wg *sync.WaitGroup, |