2022-05-24 18:17:12 +02: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 16:35:05 +02:00
"derohe-proxy/config"
2022-05-24 18:17:12 +02: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-17 00:54:30 +01:00
worker string
2022-07-19 20:44:13 +01:00
orphans uint64
hashrate float64
2022-05-24 18:17:12 +02: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 18:17:12 +02:00
var client_list_mutex sync . Mutex
var client_list = map [ * websocket . Conn ] * user_session { }
var miners_count int
2022-12-28 19:33:42 +01:00
var Shares uint64
2022-05-27 02:16:25 +02:00
var Wallet_count map [ string ] uint
2022-05-24 18:17:12 +02:00
var Address string
2022-10-03 16:35:05 +02:00
func Start_server ( ) {
2022-05-24 18:17:12 +02: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 16:35:05 +02:00
AddrsTLS : [ ] string { config . Listen_addr } ,
2022-05-24 18:17:12 +02:00
TLSConfig : tlsConfig ,
Handler : mux ,
MaxLoad : 10 * 1024 ,
2022-11-17 00:54:30 +01:00
MaxWriteBufferSize : 5 * 1024 * 1024 ,
2022-05-24 18:17:12 +02: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
}
2022-05-27 02:16:25 +02:00
Wallet_count = make ( map [ string ] uint )
2022-05-24 18:17:12 +02:00
server . Wait ( )
defer server . Stop ( )
}
func CountMiners ( ) int {
client_list_mutex . Lock ( )
defer client_list_mutex . Unlock ( )
2022-11-17 00:54:30 +01:00
2022-05-24 18:17:12 +02:00
miners_count = len ( client_list )
2022-11-17 00:54:30 +01:00
2022-05-24 18:17:12 +02:00
return miners_count
}
// forward all incoming templates from daemon to all miners
2022-10-03 16:35:05 +02:00
func SendTemplateToNodes ( data [ ] byte ) {
2022-05-24 18:17:12 +02:00
2022-05-28 13:11:49 +02:00
client_list_mutex . Lock ( )
defer client_list_mutex . Unlock ( )
2022-05-24 18:17:12 +02:00
for rk , rv := range client_list {
if client_list == nil {
break
}
2022-10-03 16:35:05 +02:00
if ! config . Pool_mode {
miner_address := rv . address_sum
2022-05-27 02:16:25 +02:00
2022-10-03 16:35:05 +02: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 18:17:12 +02: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-17 00:54:30 +01: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 18:17:12 +02: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-17 00:54:30 +01:00
session := user_session { address : * addr , address_sum : graviton . Sum ( addr_raw ) , worker : worker }
2022-05-24 18:17:12 +02:00
wsConn . SetSession ( & session )
client_list_mutex . Lock ( )
defer client_list_mutex . Unlock ( )
2022-10-03 16:35:05 +02:00
2022-05-24 18:17:12 +02:00
client_list [ wsConn ] = & session
2022-05-27 02:16:25 +02:00
Wallet_count [ client_list [ wsConn ] . address . String ( ) ] ++
2022-10-03 16:35:05 +02:00
if config . WalletAddr != "" {
Address = config . WalletAddr
} else {
Address = address
}
if ! config . Pool_mode {
2022-11-17 00:54:30 +01:00
fmt . Printf ( "%v Incoming connection: %v (%v), Wallet: %v\n" , time . Now ( ) . Format ( time . Stamp ) , wsConn . RemoteAddr ( ) . String ( ) , worker , address )
2022-10-03 16:35:05 +02:00
} else {
2022-11-17 00:54:30 +01:00
fmt . Printf ( "%v Incoming connection: %v (%v)\n" , time . Now ( ) . Format ( time . Stamp ) , wsConn . RemoteAddr ( ) . String ( ) , worker )
2022-10-03 16:35:05 +02:00
}
2022-05-24 18:17:12 +02: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 16:35:05 +02:00
/ *
var x MinerInfo_Params
if json . Unmarshal ( data , & x ) ; len ( x . Wallet_Address ) > 0 {
if x . Miner_Hashrate > 0 {
sess := client_list [ c ]
sess . hashrate = x . Miner_Hashrate
client_list [ c ] = sess
}
var NewHashRate float64
for _ , s := range client_list {
NewHashRate += s . hashrate
}
Hashrate = NewHashRate
// Update miners information
return
} else {
* /
SendToDaemon ( data )
if ! config . Pool_mode {
2022-11-17 00:54:30 +01: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 16:35:05 +02:00
} else {
2022-12-28 19:33:42 +01:00
Shares ++
2022-07-19 20:44:13 +01:00
}
2022-10-03 16:35:05 +02:00
//}
2022-05-24 18:17:12 +02:00
} )
u . OnClose ( func ( c * websocket . Conn , err error ) {
client_list_mutex . Lock ( )
defer client_list_mutex . Unlock ( )
2022-05-27 02:16:25 +02:00
Wallet_count [ client_list [ c ] . address . String ( ) ] --
2022-11-17 00:54:30 +01:00
fmt . Printf ( "%v Lost connection: %v (%v)\n" , time . Now ( ) . Format ( time . Stamp ) , c . RemoteAddr ( ) . String ( ) , client_list [ c ] . worker )
2022-05-24 18:17:12 +02:00
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
}