├── .gitignore ├── config.json_example ├── index.html ├── package.json ├── proxy.js ├── readme.md └── run.sh /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | config.json 3 | logfile.txt 4 | debuglog.txt 5 | 6 | -------------------------------------------------------------------------------- /config.json_example: -------------------------------------------------------------------------------- 1 | { 2 | "workerport":2349, 3 | "httpport":2350, 4 | "default":"nh", 5 | "pools": 6 | { 7 | "userA":[ 8 | { 9 | "symbol":"itns", 10 | "name":"izlkfjdflkjgdlfkgjdflkgjdlkfgj", 11 | "host":"45.32.171.89", 12 | "port":"9999", 13 | "url":"http://dummypool.com", 14 | "api":"http://a.x.y.z/api" 15 | },{ 16 | "symbol":"xun", 17 | "name":"Xunhfkjfhlkdfjglkdfjgdlkfgjdlkfgjdflkg", 18 | "host":"78.47.92.206", 19 | "port":"9999", 20 | "url":"http://dummypool.com", 21 | "api":"http://a.x.y.z/api" 22 | },{ 23 | "symbol":"etn", 24 | "name":"etndfglkdjfgldkjfgkldjfglkjfg", 25 | "host":"pool.etn.spacepools.org", 26 | "port":"7777", 27 | "url":"http://dummypool.com", 28 | "api":"http://a.x.y.z/api" 29 | },{ 30 | "symbol":"msr", 31 | "name":"5q9dflkjdflgkdjfgdfgdfgdfgdfg", 32 | "host":"masari.superpools.net", 33 | "port":"8889", 34 | "url":"http://dummypool.com", 35 | "api":"http://a.x.y.z/api" 36 | },{ 37 | "symbol":"nh", 38 | "name":"38efCkzYVUrojQAjuBAQHpudVpKKuaqxrZ.pr", 39 | "host":"cryptonight.eu.nicehash.com", 40 | "port":"3355" 41 | } 42 | ],"userB":[ 43 | { 44 | "symbol":"itns", 45 | "name":"izlkfjdflkjgdlfkgjdflkgjdlkfgj", 46 | "host":"45.32.171.89", 47 | "port":"9999", 48 | "url":"http://dummypool.com", 49 | "api":"http://a.x.y.z/api" 50 | },{ 51 | "symbol":"nh", 52 | "name":"38efCkzYVUrojQAjuBAQHpudVpKKuaqxrZ.pr", 53 | "host":"cryptonight.eu.nicehash.com", 54 | "port":"3355" 55 | } 56 | ] 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CryptoKnight Proxy Mining Switch 5 | 6 | 7 | 8 | 9 | 10 | 141 | 142 | 143 | 201 | 202 | 203 | 204 |
205 | 206 | 209 | 210 |
211 | 212 | 215 | 216 |
217 |
218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cryptonote-proxy", 3 | "version": "4.0.0", 4 | "license": "0BSD", 5 | "authors": "sebseb", 6 | "main": "proxy.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/sebseb7/cryptonote-proxy.git" 10 | }, 11 | "dependencies": { 12 | "bignumber.js": "^5.0.0", 13 | "body-parser": "^1.18.2", 14 | "express": "^4.16.2", 15 | "socket.io": "^2.0.4", 16 | "winston": "^2.4.0" 17 | }, 18 | "engines": { 19 | "node": ">=4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /proxy.js: -------------------------------------------------------------------------------- 1 | const net = require("net"); 2 | const events = require('events'); 3 | const fs = require('fs'); 4 | const express = require('express'); 5 | const app = require('express')(); 6 | const http = require('http'); 7 | const path = require('path'); 8 | const winston = require('winston'); 9 | const BN = require('bignumber.js'); 10 | const diff2 = BN('ffffffff', 16); 11 | 12 | 13 | const server = http.createServer(app); 14 | const io = require('socket.io').listen(server); 15 | const bodyParser = require('body-parser'); 16 | app.use(bodyParser.json()); // support json encoded bodies 17 | app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies 18 | 19 | app.get('/', function(req, res) { 20 | res.sendFile(path.resolve(__dirname+'/index.html')); 21 | }); 22 | 23 | const logger = new (winston.Logger)({ 24 | transports: [ 25 | new winston.transports.Console({timestamp:(new Date()).toLocaleTimeString(),colorize:true,level:'info'}), 26 | new winston.transports.File({name:'a',json:false,filename:'logfile.txt',timestamp:(new Date()).toLocaleTimeString(),level:'debug'}), 27 | ] 28 | }); 29 | 30 | const switchEmitter = new events.EventEmitter(); 31 | switchEmitter.setMaxListeners(200); 32 | 33 | process.on("uncaughtException", function(error) { 34 | logger.error(error); 35 | }); 36 | 37 | var config = JSON.parse(fs.readFileSync('config.json')); 38 | const localport = config.workerport; 39 | var pools = config.pools; 40 | 41 | var workerhashrates = {}; 42 | 43 | logger.info("start http interface on port %d ", config.httpport); 44 | server.listen(config.httpport,'::'); 45 | 46 | function attachPool(localsocket,coin,firstConn,setWorker,user,pass) { 47 | 48 | var idx; 49 | for (var pool in pools[user]) if (pools[user][pool].symbol === coin) idx = pool; 50 | 51 | logger.info('connect to %s %s ('+pass+')',pools[user][idx].host, pools[user][idx].port); 52 | 53 | var remotesocket = new net.Socket(); 54 | remotesocket.connect(pools[user][idx].port, pools[user][idx].host); 55 | 56 | var poolDiff=0; 57 | const connectTime = ((new Date).getTime())/1000; 58 | var shares=0; 59 | 60 | remotesocket.on('connect', function (data) { 61 | 62 | if(data) logger.debug('received from pool ('+coin+') on connect:'+data.toString().trim()+' ('+pass+')'); 63 | 64 | logger.info('new login to '+coin+' ('+pass+')'); 65 | var request = {"id":1,"method":"login","params":{"login":pools[user][idx].name,"pass":pass,"agent":"SRBMiner Cryptonight AMD GPU miner/1.6.8"}}; 66 | remotesocket.write(JSON.stringify(request)+"\n"); 67 | 68 | }); 69 | 70 | remotesocket.on('data', function(data) { 71 | 72 | if(data)logger.debug('received from pool ('+coin+'):'+data.toString().trim()+' ('+pass+')'); 73 | 74 | var request = JSON.parse(data); 75 | 76 | 77 | if(request.result && request.result.job) 78 | { 79 | var mybuf = new Buffer(request.result.job.target, "hex"); 80 | 81 | poolDiff = diff2.div(BN(mybuf.reverse().toString('hex'),16)).toFixed(0); 82 | 83 | logger.info('login reply from '+coin+' ('+pass+') (diff: '+poolDiff+')'); 84 | 85 | setWorker(request.result.id); 86 | 87 | if(! firstConn) 88 | { 89 | 90 | logger.info(' new job from login reply ('+pass+')'); 91 | var job = request.result.job; 92 | 93 | 94 | request = { 95 | "jsonrpc":"2.0", 96 | "method":"job", 97 | "params":job 98 | }; 99 | } 100 | firstConn=false; 101 | } 102 | else if(request.result && request.result.status === 'OK') 103 | { 104 | logger.info(' share deliverd to '+coin+' '+request.result.status+' ('+pass+')'); 105 | } 106 | else if(request.method && request.method === 'job') 107 | { 108 | var mybuf = new Buffer(request.params.target, "hex"); 109 | poolDiff = diff2.div(BN(mybuf.reverse().toString('hex'),16)).toFixed(0); 110 | 111 | logger.info('New Job from pool '+coin+' ('+pass+') (diff: '+poolDiff+')'); 112 | } 113 | else if(request.method) 114 | { 115 | logger.info(request.method+' (?) from pool '+coin+' ('+pass+')'); 116 | }else{ 117 | logger.info(data+' (else) from '+coin+' '+JSON.stringify(request)+' ('+pass+')'); 118 | } 119 | 120 | localsocket.write(JSON.stringify(request)+"\n"); 121 | }); 122 | 123 | remotesocket.on('close', function(had_error,text) { 124 | logger.info("pool conn to "+coin+" ended ("+pass+')'); 125 | if(workerhashrates[user]) delete workerhashrates[user][pass]; 126 | if(had_error) logger.error(' --'+text); 127 | }); 128 | remotesocket.on('error', function(text) { 129 | logger.error("pool error "+coin+' ('+pass+')',text); 130 | 131 | //set pool dirty of happens multiple times 132 | //send share reject 133 | //switchEmitter.emit('switch',coin); 134 | }); 135 | 136 | var poolCB = function(type,data){ 137 | 138 | if(type === 'stop') 139 | { 140 | if(remotesocket) remotesocket.end(); 141 | logger.info("stop pool conn to "+coin+' ('+pass+')'); 142 | } 143 | else if(type === 'push') 144 | { 145 | if(data.method && data.method === 'submit') 146 | { 147 | shares+=poolDiff/1000; 148 | 149 | const now = ((new Date).getTime())/1000; 150 | const rate = shares / (now-connectTime); 151 | 152 | if(!workerhashrates[user]) workerhashrates[user]={}; 153 | 154 | workerhashrates[user][pass]={time:now,hashrate:rate}; 155 | 156 | logger.info(' HashRate:'+((rate).toFixed(2))+' kH/s'); 157 | } 158 | remotesocket.write(JSON.stringify(data)+"\n"); 159 | } 160 | } 161 | 162 | return poolCB; 163 | }; 164 | 165 | function createResponder(localsocket,user,pass){ 166 | 167 | var myWorkerId; 168 | 169 | var connected = false; 170 | 171 | var idCB = function(id){ 172 | logger.info(' set worker response id to '+id+' ('+pass+')'); 173 | myWorkerId=id; 174 | connected = true; 175 | }; 176 | 177 | var poolCB = attachPool(localsocket,pools[user].default||config.default,true,idCB,user,pass); 178 | 179 | var switchCB = function(newcoin,newuser){ 180 | 181 | if(user!==newuser) return; 182 | 183 | logger.info('-- switch worker to '+newcoin+' ('+pass+')'); 184 | connected = false; 185 | 186 | poolCB('stop'); 187 | poolCB = attachPool(localsocket,newcoin,false,idCB,user,pass); 188 | }; 189 | 190 | switchEmitter.on('switch',switchCB); 191 | 192 | var callback = function(type,request){ 193 | 194 | if(type === 'stop') 195 | { 196 | poolCB('stop'); 197 | logger.info('disconnect from pool ('+pass+')'); 198 | switchEmitter.removeListener('switch', switchCB); 199 | } 200 | else if(request.method && request.method === 'submit') 201 | { 202 | request.params.id=myWorkerId; 203 | logger.info(' Got share from worker ('+pass+')'); 204 | 205 | var mybuf = new Buffer(request.params.result, "hex"); 206 | 207 | 208 | //logger.warn(mybuf); 209 | //var hashArray = mybuf; 210 | //var hashNum = bignum.fromBuffer(hashArray.reverse()); 211 | //var hashDiff = diff1.div(hashNum); 212 | //logger.warn(hashDiff); 213 | 214 | 215 | if(connected) poolCB('push',request); 216 | }else{ 217 | logger.info(request.method+' from worker '+JSON.stringify(request)+' ('+pass+')'); 218 | if(connected) poolCB('push',request); 219 | } 220 | } 221 | 222 | return callback; 223 | }; 224 | 225 | const workerserver = net.createServer(function (localsocket) { 226 | 227 | workerserver.getConnections(function(err,number){ 228 | logger.info(">>> connection #%d from %s:%d",number,localsocket.remoteAddress,localsocket.remotePort); 229 | }); 230 | 231 | var responderCB; 232 | 233 | localsocket.on('data', function (data) { 234 | 235 | if(data) logger.debug('received from woker ('+localsocket.remoteAddress+':'+localsocket.remotePort+'):'+data.toString().trim()); 236 | var request = JSON.parse(data); 237 | 238 | if(request.method === 'login') 239 | { 240 | logger.info('got login from worker %s %s',request.params.login,request.params.pass); 241 | responderCB = createResponder(localsocket,request.params.login,request.params.pass); 242 | 243 | }else{ 244 | if(!responderCB) 245 | { 246 | logger.warn('something before login '+JSON.stringify(request)); 247 | } 248 | else 249 | { 250 | responderCB('push',request); 251 | } 252 | } 253 | }); 254 | 255 | localsocket.on('error', function(text) { 256 | logger.error("worker error ",text); 257 | if(!responderCB) 258 | { 259 | logger.error('error before login'); 260 | } 261 | else 262 | { 263 | responderCB('stop'); 264 | } 265 | }); 266 | 267 | localsocket.on('close', function(had_error) { 268 | 269 | if(had_error) 270 | logger.error(error) 271 | else 272 | workerserver.getConnections(function(err,number){ 273 | logger.info("worker connection ended - connections left:"+number); 274 | }); 275 | 276 | if(!responderCB) 277 | { 278 | logger.warn('close before login'); 279 | } 280 | else 281 | { 282 | responderCB('stop'); 283 | } 284 | }); 285 | 286 | }); 287 | 288 | workerserver.listen(localport); 289 | 290 | logger.info("start mining proxy on port %d ", localport); 291 | 292 | io.on('connection', function(socket){ 293 | 294 | var intervalObj; 295 | 296 | socket.on('reload',function(user) { 297 | config = JSON.parse(fs.readFileSync('config.json')); 298 | pools = config.pools; 299 | 300 | var coins = []; 301 | for (var pool of pools[user]) 302 | coins.push({ 303 | symbol:pool.symbol, 304 | login:pool.name.split('.')[0], 305 | url:pool.url, 306 | api:pool.api, 307 | active:((pools[user].default||config.default)===pool.symbol)?1:0 308 | }); 309 | 310 | socket.emit('coins',coins); 311 | logger.info("pool config reloaded"); 312 | }); 313 | socket.on('user',function(user) { 314 | var coins = []; 315 | for (var pool of pools[user]) 316 | coins.push({ 317 | symbol:pool.symbol, 318 | login:pool.name.split('.')[0], 319 | url:pool.url?pool.url:'', 320 | api:pool.api?pool.api:'', 321 | active:((pools[user].default||config.default)===pool.symbol)?1:0 322 | }); 323 | 324 | socket.emit('coins',coins); 325 | logger.info('-> current for '+user+': '+(pools[user].default||config.default)); 326 | socket.emit('workers',workerhashrates[user]||{},((new Date).getTime())/1000); 327 | if(intervalObj) clearInterval(intervalObj); 328 | intervalObj = setInterval(() => { 329 | socket.emit('active',(pools[user].default||config.default)); 330 | socket.emit('workers',workerhashrates[user]||{},((new Date).getTime())/1000); 331 | }, 2000); 332 | }); 333 | 334 | socket.on('switch', function(user,coin){ 335 | logger.info('->'+coin+' ('+user+')'); 336 | pools[user].default=coin; 337 | switchEmitter.emit('switch',coin,user); 338 | socket.emit('active',coin); 339 | }); 340 | 341 | socket.on('disconnect', function(reason){ 342 | if(intervalObj) clearInterval(intervalObj); 343 | }); 344 | 345 | }); 346 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | [![NPM](https://nodei.co/npm/cryptonote-proxy.png)](https://npmjs.org/package/cryptonote-proxy) 3 | 4 | 5 | https://nodejs.org/ 6 | 7 | - install libraries with "npm i ." 8 | - adjust config.json 9 | - start with "node proxy.js" 10 | - don't open index.html from this repo, to control the switch point your browser to the "httpport" as in config.json 11 | 12 | Support https://discord.gg/h7JDXpT 13 | 14 | Windows Installtion Guide: https://github.com/sebseb7/cryptonote-proxy/wiki/Installation-guide-for-cryptonote-proxy 15 | 16 | ![switch](https://i.imgur.com/d017nWQ.png) 17 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | node proxy.js 2 | --------------------------------------------------------------------------------