From 7a0c2fc36d2d9a6b716e6418d3f37fe2f69b066c Mon Sep 17 00:00:00 2001
From: terminaldweller <thabogre@gmail.com>
Date: Thu, 7 Jul 2022 16:31:38 +0430
Subject: skeleton

---
 arbiter/Dockerfile |  20 +++++++++
 arbiter/arbiter.go | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 arbiter/go.mod     |  17 ++++++++
 arbiter/go.sum     |  30 ++++++++++++++
 hived/hived.go     |  42 +++++++++++++++++++
 5 files changed, 228 insertions(+)
 create mode 100644 arbiter/Dockerfile
 create mode 100644 arbiter/arbiter.go
 create mode 100644 arbiter/go.mod
 create mode 100644 arbiter/go.sum

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,
-- 
cgit v1.2.3