<|start_filename|>api/api.go<|end_filename|>
package api

import (
	"github.com/gorilla/mux"
	"github.com/syvita/ronin/db"
	"github.com/syvita/ronin/shared"

	"log"
	"net/http"
	"os"
)

var Database *db.Database

var logger = log.New(os.Stderr, "[API]: ", log.Ldate|log.Ltime|log.Lshortfile)

func status(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Add("Content-Type", "application/json")
	shared.Send(writer, shared.Object{"ok": true, "version": "TODO"})
}

func Listen(address string) {
	logger.Println("starting...")

	router := mux.NewRouter()

	router.HandleFunc("/status", status).Methods("GET")

	err := http.ListenAndServe(address, router)

	if err != nil {
		logger.Fatalf("failed to listen: %v", err)
	}
}


<|start_filename|>events/events.go<|end_filename|>
package events

import (
	"github.com/gorilla/mux"
	"github.com/syvita/ronin/events/mempool"
	"github.com/syvita/ronin/shared"

	"encoding/json"
	"github.com/syvita/ronin/db"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

var Database *db.Database

var logger = log.New(os.Stderr, "[EVENT]: ", log.Ldate|log.Ltime|log.Lshortfile)

func init() {
	mempool.Logger = logger
}

func NewMempoolTX(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Add("Content-Type", "application/json")

	body, err := ioutil.ReadAll(request.Body)

	if err != nil {
		writer.WriteHeader(500)
		shared.Send(writer, shared.Object{"error": "failed to read body"})
		return
	}

	if len(body) == 0 {
		writer.WriteHeader(400)
		shared.Send(writer, shared.Object{"error": "body is required"})
		return
	}

	var TXs []string

	err = json.Unmarshal(body, &TXs)

	if err != nil {
		writer.WriteHeader(400)
		shared.Send(writer, shared.Object{"error": "json is invalid"})
		return
	}

	TXIDs, err := mempool.NewTX(TXs)

	if err != nil {
		writer.WriteHeader(500)
		shared.Send(writer, shared.Object{"error": err.Error()})
		return
	}

	shared.Send(writer, shared.Object{"ok": true, "txids": TXIDs})
}

func Listen(address string) {
	logger.Println("starting...")

	router := mux.NewRouter()

	router.HandleFunc("/new_mempool_tx", NewMempoolTX).Methods("POST")

	err := http.ListenAndServe(address, router)

	if err != nil {
		logger.Fatalf("failed to listen: %v", err)
	}
}


<|start_filename|>db/leaderboard.go<|end_filename|>
package db

var leaderboardKey = "leaderboard"

type Leaderboard struct {
	Count int `json:"count"`
	Users []*User
}

func (db *Database) GetLeaderboard() (*Leaderboard, error) {
	scores := db.Client.ZRangeWithScores(Ctx, leaderboardKey, 0, -1)
	if scores == nil {
		return nil, ErrNil
	}
	count := len(scores.Val())
	users := make([]*User, count)
	for idx, member := range scores.Val() {
		users[idx] = &User{
			Username: member.Member.(string),
			Points: int(member.Score),
			Rank: idx,
		}
	}
	leaderboard := &Leaderboard{
		Count: count,
		Users: users,
	}
	return leaderboard, nil
}

<|start_filename|>db/user.go<|end_filename|>
package db

import (
	"fmt"
	"github.com/go-redis/redis/v8"
)

type User struct {
	Username string `json:"username" binding:"required"`
	Points   int    `json:"points" binding:"required"`
	Rank     int    `json:"rank"`
}

func (db *Database) SaveUser(user *User) error {
	member := &redis.Z{
		Score:  float64(user.Points),
		Member: user.Username,
	}
	pipe := db.Client.TxPipeline()
	pipe.ZAdd(Ctx, "leaderboard", member)
	rank := pipe.ZRank(Ctx, leaderboardKey, user.Username)
	_, err := pipe.Exec(Ctx)
	if err != nil {
		return err
	}
	fmt.Println(rank.Val(), err)
	user.Rank = int(rank.Val())
	return nil
}

func (db *Database) GetUser(username string) (*User, error) {
	pipe := db.Client.TxPipeline()
	score := pipe.ZScore(Ctx, leaderboardKey, username)
	rank := pipe.ZRank(Ctx, leaderboardKey, username)
	_, err := pipe.Exec(Ctx)
	if err != nil {
		return nil, err
	}
	if score == nil {
		return nil, ErrNil
	}
	return &User{
		Username: username,
		Points:   int(score.Val()),
		Rank:     int(rank.Val()),
	}, nil
}


<|start_filename|>main.go<|end_filename|>
package main

import (
	"github.com/syvita/ronin/api"
	"github.com/syvita/ronin/events"

	"github.com/syvita/ronin/db"

	"log"
	"os"
)

var RedisAddr = "localhost:6379"

var logger = log.New(os.Stderr, "[MAIN]: ", log.Ldate|log.Ltime|log.Lshortfile)

func main() {
	database, err := db.NewDatabase(RedisAddr)

	if err != nil {
		logger.Fatalf("Failed to connect to redis: %s", err.Error())
	}

	logger.Println("Connected to Redis successfully")

	events.Database = database
	api.Database = database

	// goroutines >>>>> anything else

	go api.Listen(":3999")
	go events.Listen("127.0.0.1:3700")

	for {
	}
}


<|start_filename|>shared/shared.go<|end_filename|>
package shared

import (
	"encoding/json"
	"net/http"
)

type Object map[string]interface{}

func Send(writer http.ResponseWriter, body Object) {
	err := json.NewEncoder(writer).Encode(body)

	if err != nil {
		writer.Write([]byte("failed to serialize error"))
	}
}


<|start_filename|>db/db.go<|end_filename|>
package db

import (
	"context"
	"errors"
	"github.com/go-redis/redis/v8"
)

type Database struct {
	Client *redis.Client
}

var (
	ErrNil = errors.New("no matching record found in redis database")
	// dunno why it's using a context here but i guess we'll find out
	Ctx    = context.TODO()
)

func NewDatabase(address string) (*Database, error) {
	client := redis.NewClient(&redis.Options{
		Addr: address,
		Password: "",
		DB: 0,
	})
	// ping da server
	if err := client.Ping(Ctx).Err(); err != nil {
		return nil, err
	}
	return &Database{
		Client: client,
	}, nil
}



<|start_filename|>events/mempool/mempool.go<|end_filename|>
package mempool

import (
	"crypto/sha512"
	"encoding/hex"
	"errors"
	"log"
)

var Logger = log.Default()

func NewTX(TXs []string) ([]string, error) {
	var rawTXs [][]byte
	var TXIDs []string

	// remove `0x` prefix from each tx string and decode to hex with hex.DecodeString
	for _, TX := range TXs {
		if len(TX) < 2 {
			return []string{}, errors.New("TX is too short")
		}

		rawTX, err := hex.DecodeString(TX[2:])

		if err != nil {
			return []string{}, errors.New("failed to decode TX")
		}

		rawTXs = append(rawTXs, rawTX)
		sum := sha512.Sum512_256(rawTX)
		TXIDs = append(TXIDs, hex.EncodeToString(sum[:]))
	}

	// log hex-encoded txs using hex.EncodeToString(tx)
	// here we'd probably do something with the txs
	for _, TX := range rawTXs {
		sum := sha512.Sum512_256(TX)
		TXID := hex.EncodeToString(sum[:])
		Logger.Println("new mempool tx:", hex.EncodeToString([]byte(TXID)))
	}

	return TXIDs, nil
}
