package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "os" "strings" "sync" "time" "github.com/lesismal/llib/std/crypto/tls" "github.com/deroproject/derohe/globals" "github.com/lesismal/nbio/nbhttp" "github.com/lesismal/nbio/nbhttp/websocket" "dero-swap/cfg" "dero-swap/clients" "dero-swap/coin" "dero-swap/dero" ) type WS_Message struct { ID uint64 `json:"id"` Method string `json:"method"` Params any `json:"params,omitempty"` Result any `json:"result"` } var WSConnections sync.Map // swap other coins to Dero func Dero_Swap(request coin.Swap_Request) (response coin.Swap_Response) { var err error // check if destination wallet is valid. Registered usernames can also be used. if strings.HasPrefix(request.DeroAddr, "dero1") || strings.HasPrefix(request.DeroAddr, "deroi") { _, err = globals.ParseValidateAddress(request.DeroAddr) } else { if addr := dero.CheckAddress(request.DeroAddr); addr != "" { request.DeroAddr = addr } else { err = fmt.Errorf("invalid address") } } // basic checks if request.Amount == 0 || err != nil { response.Error = "invalid request" return } // prevent users from creating too many swap requests if Delay.CheckUser(request.DeroAddr) { response.Error = "2 minutes wait time triggered" return } // check if pair is enabled and available pair := request.Pair if !coin.IsPairEnabled(pair) || !IsPairAvailable[pair] { response.Error = fmt.Sprintf("%s swap currently not possible", pair) return } // create swap err = XTCSwap(pair, request.DeroAddr, coin.RoundFloat(request.Amount, 5), &response) if err != nil { response.Error = err.Error() log.Println(err) } else { Delay.AddUser(request.DeroAddr) } response.Request = request return response } // swap Dero to other coins func Reverse_Swap(request coin.Swap_Request) (response coin.Swap_Response) { var err error // prevent users from creating too many swap requests if Delay.CheckUser(request.DeroAddr) { response.Error = "2 minutes wait time triggered" return } // check if pair is enabled and available pair := request.Pair if !coin.IsPairEnabled(pair) || !IsPairAvailable[pair] { response.Error = fmt.Sprintf("%s swap currently not possible", pair) return } response.Deposit = coin.RoundFloat(request.Amount, 5) // create swap err = DeroXTCSwap(pair, request.DeroAddr, response.Deposit, &response) if err != nil { response.Error = err.Error() log.Println(err) } else { Delay.AddUser(request.DeroAddr) } response.Request = request return response } func newUpgrader() *websocket.Upgrader { u := websocket.NewUpgrader() u.CheckOrigin = (func(r *http.Request) bool { return true }) u.OnClose(func(c *websocket.Conn, err error) { WSConnections.Delete(c) clients.Clients.Delete(c) }) u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { var in, out WS_Message var send bytes.Buffer read := bytes.NewReader(data) decoder := json.NewDecoder(read) encoder := json.NewEncoder(&send) err := decoder.Decode(&in) if err != nil { fmt.Println(err) return } out.ID = in.ID switch in.Method { case "swap": var request coin.Swap_Request var response coin.Swap_Response p := in.Params.(map[string]any) out.Method = "swap" if d, ok := p["pair"]; ok { request.Pair = d.(string) } if d, ok := p["amount"]; ok { request.Amount = d.(float64) } if d, ok := p["dero_address"]; ok { request.DeroAddr = d.(string) } if q, ok := p["extern"]; ok && q.(string) != "" { if ok, conn := clients.PrepareExternalSwap(q.(string), request.Pair, request.Amount); ok { clients.ActiveClients.ChangeClientState(clients.LOCK, conn) clients.ActiveClients.AddOrigin(conn, c) out.Params = request encoder.Encode(out) SendWSData(conn, send.Bytes()) } else { response.Error = "External swap not possible" } out.Result = response encoder.Encode(out) SendWSData(c, send.Bytes()) return } switch request.Pair { case coin.BTCDERO, coin.LTCDERO, coin.ARRRDERO, coin.XMRDERO: response = Dero_Swap(request) case coin.DEROBTC, coin.DEROLTC, coin.DEROARRR, coin.DEROXMR: response = Reverse_Swap(request) default: } out.Result = response encoder.Encode(out) SendWSData(c, send.Bytes()) var track coin.Swap_Tracking track.ID = response.ID out.Method = "track" go func() { for { time.Sleep(time.Second * 15) track.State = SwapTracking(response.ID) out.Result = track send.Reset() encoder.Encode(out) if _, ok := WSConnections.Load(c); ok { SendWSData(c, send.Bytes()) } else { break } if track.State == SWAP_DONE || track.State == SWAP_EXPIRED { break } } }() case "market": mk.RLock() defer mk.RUnlock() var data bytes.Buffer out.Method = "market" inner := mk.Pairs out.Result = inner encoder := json.NewEncoder(&data) encoder.Encode(out) SendWSData(c, data.Bytes()) case "balance": var data bytes.Buffer out.Method = "balance" out.Result = UpdatePool() encoder := json.NewEncoder(&data) encoder.Encode(out) SendWSData(c, data.Bytes()) case "client": var client clients.ClientInfo p := in.Params.(map[string]any) client.Nickname = p["nickname"].(string) q := p["pair_info"].([]any) for i := range q { r := q[i].(map[string]any) client.PairInfo = append(client.PairInfo, clients.PairInfo{Balance: r["balance"].(float64), Pair: r["pair"].(string)}) } if _, ok := clients.Clients.Load(c); !ok { log.Printf("Client Hello from %s\n", client.Nickname) } clients.Clients.Store(c, client) case "client_ok": if clients.ActiveClients.CheckClientState(c) { clients.ActiveClients.ChangeClientState(clients.UNLOCK, c) client := clients.ActiveClients.GetOrigin(c) out.Method = "swap" out.Result = in.Result encoder.Encode(out) SendWSData(client, send.Bytes()) } default: } }) return u } func webSocketHandler(w http.ResponseWriter, r *http.Request) { upgrader := newUpgrader() conn, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Println(err) return } WSConnections.Store(conn, true) } func SendWSData(c *websocket.Conn, data []byte) { if len(data) == 0 { return } if c != nil { err := c.WriteMessage(websocket.TextMessage, data) if err != nil { log.Println(err) } } else { WSConnections.Range(func(k any, v any) bool { conn := k.(*websocket.Conn) err := conn.WriteMessage(websocket.TextMessage, data) if err != nil { log.Println(err) } return true }) } } func StartServer() { // cert files // Let's Encrypt: // certFile = fullchain.pem // keyFile = privkey.pem // comment the following block to disable TLS cert, err := tls.LoadX509KeyPair("/etc/letsencrypt/live/mg25ot4aggt8dprv.myfritz.net/fullchain.pem", "/etc/letsencrypt/live/mg25ot4aggt8dprv.myfritz.net/privkey.pem") if err != nil { log.Println(err) os.Exit(2) } mux := http.NewServeMux() mux.HandleFunc("/ws", webSocketHandler) //mux.HandleFunc("/api", ) srv := nbhttp.NewServer(nbhttp.Config{ Network: "tcp", Handler: mux, // comment the following 2 lines and uncomment "Addrs" to start server without TLS AddrsTLS: []string{cfg.Settings.ListenAddress}, TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, //Addrs: []string{cfg.Settings.ListenAddress}, }) err = srv.Start() if err != nil { log.Println(err) os.Exit(2) } srv.Wait() }