derohe-proxy/proxy/server.go

317 lines
7.7 KiB
Go
Raw Normal View History

2022-05-24 17:17:12 +01:00
package proxy
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math/big"
"net/http"
"runtime"
"strings"
"sync"
"time"
2022-10-03 15:35:05 +01:00
"derohe-proxy/config"
2022-05-24 17:17:12 +01:00
"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
2022-11-16 23:54:30 +00:00
worker string
2022-07-19 20:44:13 +01:00
orphans uint64
hashrate float64
2022-05-24 17:17:12 +01:00
valid_address bool
address_sum [32]byte
}
2022-07-19 20:44:13 +01:00
type ( // array without name containing block template in hex
MinerInfo_Params struct {
Wallet_Address string `json:"wallet_address"`
Miner_Tag string `json:"miner_tag"`
Miner_Hashrate float64 `json:"miner_hashrate"`
}
MinerInfo_Result struct {
}
)
2022-05-24 17:17:12 +01:00
var client_list_mutex sync.Mutex
var client_list = map[*websocket.Conn]*user_session{}
var miners_count int
2022-12-28 18:33:42 +00:00
var Shares uint64
2023-03-27 22:13:50 +01:00
var shareValue uint64
var Wallet_count map[string]uint
2022-05-24 17:17:12 +01:00
var Address string
2023-03-27 22:13:50 +01:00
var rwmutex sync.RWMutex
2022-10-03 15:35:05 +01:00
func Start_server() {
2022-05-24 17:17:12 +01:00
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",
2022-10-03 15:35:05 +01:00
AddrsTLS: []string{config.Listen_addr},
2022-05-24 17:17:12 +01:00
TLSConfig: tlsConfig,
Handler: mux,
MaxLoad: 10 * 1024,
2022-11-16 23:54:30 +00:00
MaxWriteBufferSize: 5 * 1024 * 1024,
2022-05-24 17:17:12 +01:00
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
}
Wallet_count = make(map[string]uint)
2022-05-24 17:17:12 +01:00
server.Wait()
defer server.Stop()
}
func CountMiners() int {
client_list_mutex.Lock()
defer client_list_mutex.Unlock()
2022-11-16 23:54:30 +00:00
2022-05-24 17:17:12 +01:00
miners_count = len(client_list)
2022-11-16 23:54:30 +00:00
2022-05-24 17:17:12 +01:00
return miners_count
}
// forward all incoming templates from daemon to all miners
2022-10-03 15:35:05 +01:00
func SendTemplateToNodes(data []byte) {
2022-05-24 17:17:12 +01:00
2022-05-28 12:11:49 +01:00
client_list_mutex.Lock()
defer client_list_mutex.Unlock()
2022-05-24 17:17:12 +01:00
for rk, rv := range client_list {
if client_list == nil {
break
}
2022-10-03 15:35:05 +01:00
if !config.Pool_mode {
miner_address := rv.address_sum
2022-10-03 15:35:05 +01:00
if result := edit_blob(data, miner_address, config.Nonce); result != nil {
data = result
} else {
fmt.Println(time.Now().Format(time.Stamp), "Failed to change nonce / miner keyhash")
}
2022-05-24 17:17:12 +01:00
}
go func(k *websocket.Conn, v *user_session) {
defer globals.Recover(2)
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/")
2022-11-16 23:54:30 +00:00
// check for worker suffix
var parseWorker []string
var worker string
if strings.Contains(address, ".") {
parseWorker = strings.Split(address, ".")
worker = parseWorker[1]
address = parseWorker[0]
}
2022-05-24 17:17:12 +01:00
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)
2022-11-16 23:54:30 +00:00
session := user_session{address: *addr, address_sum: graviton.Sum(addr_raw), worker: worker}
2022-05-24 17:17:12 +01:00
wsConn.SetSession(&session)
client_list_mutex.Lock()
defer client_list_mutex.Unlock()
2022-10-03 15:35:05 +01:00
2022-05-24 17:17:12 +01:00
client_list[wsConn] = &session
Wallet_count[client_list[wsConn].address.String()]++
2022-10-03 15:35:05 +01:00
if config.WalletAddr != "" {
Address = config.WalletAddr
} else {
Address = address
}
if !config.Pool_mode {
2022-11-16 23:54:30 +00:00
fmt.Printf("%v Incoming connection: %v (%v), Wallet: %v\n", time.Now().Format(time.Stamp), wsConn.RemoteAddr().String(), worker, address)
2022-10-03 15:35:05 +01:00
} else {
2022-11-16 23:54:30 +00:00
fmt.Printf("%v Incoming connection: %v (%v)\n", time.Now().Format(time.Stamp), wsConn.RemoteAddr().String(), worker)
2023-03-27 22:13:50 +01:00
if len(client_list) == 1 {
Connected = time.Now().UnixMilli()
shareValue = 0
}
2022-10-03 15:35:05 +01:00
}
2022-05-24 17:17:12 +01:00
}
// 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()
2022-10-03 15:35:05 +01:00
SendToDaemon(data)
if !config.Pool_mode {
2022-11-16 23:54:30 +00:00
fmt.Printf("%v Submitting result from miner: %v (%v), Wallet: %v\n", time.Now().Format(time.Stamp), c.RemoteAddr().String(), client_list[c].worker, client_list[c].address.String())
2022-10-03 15:35:05 +01:00
} else {
2022-12-28 18:33:42 +00:00
Shares++
2023-03-27 22:13:50 +01:00
shareValue += difficulty
if Connected > 0 {
Hashrate = shareValue / (uint64(time.Now().UnixMilli()-Connected) / 1000)
}
2022-07-19 20:44:13 +01:00
}
2022-10-03 15:35:05 +01:00
//}
2022-05-24 17:17:12 +01:00
})
u.OnClose(func(c *websocket.Conn, err error) {
client_list_mutex.Lock()
defer client_list_mutex.Unlock()
Wallet_count[client_list[c].address.String()]--
2022-11-16 23:54:30 +00:00
fmt.Printf("%v Lost connection: %v (%v)\n", time.Now().Format(time.Stamp), c.RemoteAddr().String(), client_list[c].worker)
2022-05-24 17:17:12 +01:00
delete(client_list, c)
})
return u
}
2023-03-27 22:13:50 +01:00
func CountWallets() {
rwmutex.RLock()
defer rwmutex.RUnlock()
for i := range Wallet_count {
if Wallet_count[i] > 1 {
fmt.Printf("%v Wallet %v, %d miners\n", time.Now().Format(time.Stamp), i, Wallet_count[i])
}
}
}
2022-05-24 17:17:12 +01:00
// 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
}