diff --git a/client.go b/client.go new file mode 100644 index 0000000..d5b2e43 --- /dev/null +++ b/client.go @@ -0,0 +1,53 @@ +package proxy + +import ( + "crypto/tls" + "fmt" + "net/url" + "time" + + "github.com/gorilla/websocket" +) + +var connection *websocket.Conn + +// proxy-client +func Start_client(v string, w string) { + var err error + + for { + + u := url.URL{Scheme: "wss", Host: v, Path: "/ws/" + w} + + dialer := websocket.DefaultDialer + dialer.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + + fmt.Println("Connect to node", v, "using wallet address", w) + connection, _, err = websocket.DefaultDialer.Dial(u.String(), nil) + if err != nil { + time.Sleep(5 * time.Second) + fmt.Println(err) + continue + } + + for { + msg_type, recv_data, err := connection.ReadMessage() + if msg_type != websocket.TextMessage || err != nil { + break + } else { + fmt.Println(string(recv_data)) + //edit_blob(recv_data) + go SendTemplatesToNode(recv_data) + } + } + + } +} + +func SendToDaemon(buffer []byte) { + connection.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + connection.WriteMessage(websocket.TextMessage, buffer) + return +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..dd7a10e --- /dev/null +++ b/server.go @@ -0,0 +1,233 @@ +package proxy + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/x509" + "encoding/pem" + "fmt" + "math/big" + "net/http" + "runtime" + "strings" + "sync" + "time" + + "github.com/deroproject/derohe/globals" + "github.com/deroproject/derohe/rpc" + "github.com/deroproject/graviton" + "github.com/lesismal/llib/std/crypto/tls" + "github.com/lesismal/nbio" + "github.com/lesismal/nbio/nbhttp" + "github.com/lesismal/nbio/nbhttp/websocket" +) + +var server *nbhttp.Server + +var memPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 16*1024) + }, +} + +type user_session struct { + blocks uint64 + miniblocks uint64 + lasterr string + address rpc.Address + valid_address bool + address_sum [32]byte +} + +var client_list_mutex sync.Mutex +var client_list = map[*websocket.Conn]*user_session{} + +var miners_count int +var Address string + +func Start_server(listen string) { + var err error + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{generate_random_tls_cert()}, + InsecureSkipVerify: true, + } + + mux := &http.ServeMux{} + mux.HandleFunc("/", onWebsocket) // handle everything + + server = nbhttp.NewServer(nbhttp.Config{ + Name: "GETWORK", + Network: "tcp", + AddrsTLS: []string{listen}, + TLSConfig: tlsConfig, + Handler: mux, + MaxLoad: 10 * 1024, + MaxWriteBufferSize: 32 * 1024, + ReleaseWebsocketPayload: true, + KeepaliveTime: 240 * time.Hour, // we expects all miners to find a block every 10 days, + NPoller: runtime.NumCPU(), + }) + + server.OnReadBufferAlloc(func(c *nbio.Conn) []byte { + return memPool.Get().([]byte) + }) + server.OnReadBufferFree(func(c *nbio.Conn, b []byte) { + memPool.Put(b) + }) + + if err = server.Start(); err != nil { + return + } + + server.Wait() + defer server.Stop() + +} + +func CountMiners() int { + client_list_mutex.Lock() + defer client_list_mutex.Unlock() + miners_count = len(client_list) + return miners_count +} + +// forward all incoming templates from daemon to all miners +func SendTemplatesToNode(data []byte) { + + for rk, rv := range client_list { + + if client_list == nil { + break + } + + go func(k *websocket.Conn, v *user_session) { + defer globals.Recover(2) + fmt.Printf("%4d miners connected\r", CountMiners()) + k.SetWriteDeadline(time.Now().Add(100 * time.Millisecond)) + k.WriteMessage(websocket.TextMessage, data) + + }(rk, rv) + + } + +} + +// handling for incoming miner connections +func onWebsocket(w http.ResponseWriter, r *http.Request) { + if !strings.HasPrefix(r.URL.Path, "/ws/") { + http.NotFound(w, r) + return + } + address := strings.TrimPrefix(r.URL.Path, "/ws/") + + addr, err := globals.ParseValidateAddress(address) + if err != nil { + fmt.Fprintf(w, "err: %s\n", err) + return + } + + upgrader := newUpgrader() + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + //panic(err) + return + } + + addr_raw := addr.PublicKey.EncodeCompressed() + wsConn := conn.(*websocket.Conn) + + session := user_session{address: *addr, address_sum: graviton.Sum(addr_raw)} + wsConn.SetSession(&session) + + client_list_mutex.Lock() + defer client_list_mutex.Unlock() + client_list[wsConn] = &session + Address = address +} + +// forward results to daemon +func newUpgrader() *websocket.Upgrader { + u := websocket.NewUpgrader() + + u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { + + if messageType != websocket.TextMessage { + return + } + + client_list_mutex.Lock() + defer client_list_mutex.Unlock() + + SendToDaemon(data) + fmt.Println("Submit result to node") + }) + + u.OnClose(func(c *websocket.Conn, err error) { + client_list_mutex.Lock() + defer client_list_mutex.Unlock() + delete(client_list, c) + + }) + + return u +} + +// taken unmodified from derohe repo +// cert handling +func generate_random_tls_cert() tls.Certificate { + + /* RSA can do only 500 exchange per second, we need to be faster + * reference https://github.com/golang/go/issues/20058 + key, err := rsa.GenerateKey(rand.Reader, 512) // current using minimum size + if err != nil { + log.Fatal("Private key cannot be created.", err.Error()) + } + + // Generate a pem block with the private key + keyPem := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(key), + }) + */ + // EC256 does roughly 20000 exchanges per second + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + b, err := x509.MarshalECPrivateKey(key) + if err != nil { + panic(err) + } + // Generate a pem block with the private key + keyPem := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}) + + tml := x509.Certificate{ + SerialNumber: big.NewInt(int64(time.Now().UnixNano())), + + // TODO do we need to add more parameters to make our certificate more authentic + // and thwart traffic identification as a mass scale + + // you can add any attr that you need + NotBefore: time.Now().AddDate(0, -1, 0), + NotAfter: time.Now().AddDate(1, 0, 0), + // you have to generate a different serial number each execution + /* + Subject: pkix.Name{ + CommonName: "New Name", + Organization: []string{"New Org."}, + }, + BasicConstraintsValid: true, // even basic constraints are not required + */ + } + cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key) + if err != nil { + panic(err) + } + + // Generate a pem block with the certificate + certPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) + tlsCert, err := tls.X509KeyPair(certPem, keyPem) + if err != nil { + panic(err) + } + return tlsCert +}