From fcc3ec7600305099715542ba5546f58a2fd8ec71 Mon Sep 17 00:00:00 2001 From: terminaldweller Date: Wed, 20 Jul 2022 20:17:57 +0430 Subject: added a coingecko endpoint --- arbiter/Dockerfile | 6 +- arbiter/arbiter.go | 165 +++++++++++++++++++++++++++++++++++++++++------ arbiter/tests.sh | 3 + docker-compose-test.yaml | 13 ++++ docker-compose.yaml | 13 ++++ hived/Dockerfile | 6 +- hived/hived.go | 10 +-- telebot/Dockerfile | 6 +- 8 files changed, 187 insertions(+), 35 deletions(-) create mode 100755 arbiter/tests.sh diff --git a/arbiter/Dockerfile b/arbiter/Dockerfile index fe3fb31..849b345 100644 --- a/arbiter/Dockerfile +++ b/arbiter/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder ENV GOPROXY=https://goproxy.io RUN apk update && apk upgrade RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /arbiter && go mod download COPY *.go /arbiter/ RUN cd /arbiter && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 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 +FROM alpine:3.16 COPY --from=certbuilder /certs /certs COPY --from=builder /arbiter/arbiter /arbiter/ ENTRYPOINT ["/arbiter/arbiter"] diff --git a/arbiter/arbiter.go b/arbiter/arbiter.go index 2806842..9cb3ad0 100644 --- a/arbiter/arbiter.go +++ b/arbiter/arbiter.go @@ -3,12 +3,16 @@ package main import ( "context" "crypto/tls" + "encoding/json" "errors" "flag" "fmt" + "io/ioutil" "net/http" + "net/url" "os" "os/signal" + "sync" "time" "github.com/go-redis/redis/v8" @@ -28,8 +32,26 @@ var ( const ( SERVER_DEPLOYMENT_TYPE = "SERVER_DEPLOYMENT_TYPE" + coingeckoAPIURLv3 = "https://api.coingecko.com/api/v3" ) +type HttpHandlerFunc func(http.ResponseWriter, *http.Request) + +type HttpHandler struct { + name string + function HttpHandlerFunc +} + +type priceChanStruct struct { + name string + price float64 +} + +type errorChanStruct struct { + hasError bool + err error +} + // OWASP: https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html func addSecureHeaders(w *http.ResponseWriter) { (*w).Header().Set("Cache-Control", "no-store") @@ -40,49 +62,152 @@ func addSecureHeaders(w *http.ResponseWriter) { (*w).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") } +//binance +func getPriceFromBinance(name, unit string, + wg *sync.WaitGroup, + priceChan chan<- priceChanStruct, + errChan chan<- errorChanStruct) { + +} + +//kucoin +func getPriceFromKu(name, uni string, + wg *sync.WaitGroup, + priceChan chan<- priceChanStruct, + errChan chan<- errorChanStruct) { + +} + +func getPriceFromCoinGecko( + name, unit string, + wg *sync.WaitGroup, + priceChan chan<- priceChanStruct, + errChan chan<- errorChanStruct) { + defer wg.Done() + + params := "/simple/price?ids=" + url.QueryEscape(name) + "&" + + "vs_currencies=" + url.QueryEscape(unit) + path := coingeckoAPIURLv3 + params + fmt.Println(path) + 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 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 + + var name string + var unit string + params := r.URL.Query() + for key, value := range params { + switch key { + case "name": + name = value[0] + case "unit": + unit = value[0] + default: + log.Error().Err(errors.New("Got unexpected parameter.")) + } + } + + priceChan := make(chan priceChanStruct, 1) + errChan := make(chan errorChanStruct, 1) + var wg sync.WaitGroup + wg.Add(1) + getPriceFromCoinGecko(name, unit, &wg, priceChan, errChan) + wg.Wait() + + select { + case err := <-errChan: + if err.hasError != false { + log.Error().Err(err.err) + } + default: + log.Error().Err(errors.New("We shouldnt be here")) + } + + var price priceChanStruct + select { + case priceCh := <-priceChan: + price = priceCh + default: + log.Fatal().Err(errors.New("We shouldnt be here")) + } + + json.NewEncoder(w).Encode(map[string]interface{}{ + "name": price.name, + "price": price.price, + "unit": unit, + "err": "", + "isSuccessful": true, + }) } -func startServer(gracefulWait time.Duration) { +func setupLogging() { + zerolog.TimeFieldFormat = zerolog.TimeFormatUnix +} + +func startServer(gracefulWait time.Duration, + handlers []HttpHandler, + serverDeploymentType string, port string) { 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, + Addr: "0.0.0.0:" + port, WriteTimeout: time.Second * 15, ReadTimeout: time.Second * 15, Handler: r, TLSConfig: cfg, } - r.HandleFunc("/crypto/arb", arbHandler) + + for i := 0; i < len(handlers); i++ { + r.HandleFunc(handlers[i].name, handlers[i].function) + } go func() { var certPath, keyPath string - if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "deployment" { + if os.Getenv(serverDeploymentType) == "deployment" { certPath = "/certs/fullchain1.pem" keyPath = "/certs/privkey1.pem" - } else if os.Getenv(SERVER_DEPLOYMENT_TYPE) == "test" { + } else if os.Getenv(serverDeploymentType) == "test" { certPath = "/certs/server.cert" keyPath = "/certs/server.key" } else { - log.Fatal().Err(errors.New(fmt.Sprintf("unknown deployment kind: %s", SERVER_DEPLOYMENT_TYPE))) + log.Fatal().Err(errors.New(fmt.Sprintf("unknown deployment kind: %s", serverDeploymentType))) } if err := srv.ListenAndServeTLS(certPath, keyPath); err != nil { log.Fatal().Err(err) @@ -99,10 +224,6 @@ func startServer(gracefulWait time.Duration) { 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") @@ -115,5 +236,7 @@ func main() { defer rdb.Close() setupLogging() - startServer(gracefulWait) + var handlerFuncs = []HttpHandler{{name: "/arb", function: arbHandler}} + + startServer(gracefulWait, handlerFuncs, SERVER_DEPLOYMENT_TYPE, *flagPort) } diff --git a/arbiter/tests.sh b/arbiter/tests.sh new file mode 100755 index 0000000..1fe9792 --- /dev/null +++ b/arbiter/tests.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +curl -X GET "https://localhost:8009/arb?name=ethereum&unit=usd" diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml index afee17a..99d9a76 100644 --- a/docker-compose-test.yaml +++ b/docker-compose-test.yaml @@ -36,6 +36,19 @@ services: - ALL environment: - SERVER_DEPLOYMENT_TYPE=test + arbiter: + image: arbiter + build: + context: ./arbiter + networks: + - mainnet + ports: + - "8009:8009" + entrypoint: ["/arbiter/arbiter"] + cap_drop: + - ALL + environment: + - SERVER_DEPLOYMENT_TYPE=test redis: image: redis:6.2-alpine networks: diff --git a/docker-compose.yaml b/docker-compose.yaml index 90ff90d..7ce5495 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -51,6 +51,19 @@ services: - ALLOW_EMPTY_PASSWORD=yes volumes: - redis-data:/data/ + arbiter: + image: arbiter + build: + context: ./arbiter + networks: + - mainnet + ports: + - "8009:8009" + entrypoint: ["/arbiter/abiter"] + cap_drop: + - ALL + environment: + - SERVER_DEPLOYMENT_TYPE=deployment networks: mainnet: driver: bridge diff --git a/hived/Dockerfile b/hived/Dockerfile index 7eb1a8d..1eaf999 100644 --- a/hived/Dockerfile +++ b/hived/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder ENV GOPROXY=https://goproxy.io RUN apk update && apk upgrade RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /hived && go mod download COPY *.go /hived/ RUN cd /hived && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 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 +FROM alpine:3.16 COPY --from=certbuilder /certs /certs COPY --from=builder /hived/hived /hived/ COPY ./docker-entrypoint.sh /hived/ diff --git a/hived/hived.go b/hived/hived.go index 940c2ed..a205352 100644 --- a/hived/hived.go +++ b/hived/hived.go @@ -130,7 +130,7 @@ func getPriceFromCoinGecko( errChan <- errorChanStruct{hasError: false, err: nil} } -func sendGetToCryptoCompare( +func getPriceFromCryptoCompare( name, unit string, wg *sync.WaitGroup, priceChan chan<- priceChanStruct, @@ -204,7 +204,7 @@ func priceHandler(w http.ResponseWriter, r *http.Request) { defer close(errChan) defer close(priceChan) wg.Add(1) - go sendGetToCryptoCompare(name, unit, &wg, priceChan, errChan) + go getPriceFromCryptoCompare(name, unit, &wg, priceChan, errChan) wg.Wait() select { @@ -271,8 +271,8 @@ func pairHandler(w http.ResponseWriter, r *http.Request) { defer close(errChan) wg.Add(2) - go sendGetToCryptoCompare(one, "USD", &wg, priceChan, errChan) - go sendGetToCryptoCompare(two, "USD", &wg, priceChan, errChan) + go getPriceFromCryptoCompare(one, "USD", &wg, priceChan, errChan) + go getPriceFromCryptoCompare(two, "USD", &wg, priceChan, errChan) wg.Wait() for i := 0; i < 2; i++ { @@ -362,7 +362,7 @@ func alertManager() { wg.Add(len(vars)) for i := range vars { - go sendGetToCryptoCompare(vars[i], "USD", &wg, priceChan, errChan) + go getPriceFromCryptoCompare(vars[i], "USD", &wg, priceChan, errChan) } wg.Wait() diff --git a/telebot/Dockerfile b/telebot/Dockerfile index d358ed7..a3cebf9 100644 --- a/telebot/Dockerfile +++ b/telebot/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 as builder +FROM alpine:3.16 as builder ENV GOPROXY=https://goproxy.io RUN apk update && apk upgrade RUN apk add go git @@ -8,13 +8,13 @@ RUN cd /telebot && go mod download COPY *.go /telebot/ RUN cd /telebot && go build -FROM alpine:3.15 as certbuilder +FROM alpine:3.16 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 +FROM alpine:3.16 COPY --from=certbuilder /certs /certs COPY --from=builder /telebot/telebot /telebot/ COPY ./docker-entrypoint.sh /telebot/ -- cgit v1.2.3