├── .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 | [](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 | 
17 |
--------------------------------------------------------------------------------
/run.sh:
--------------------------------------------------------------------------------
1 | node proxy.js
2 |
--------------------------------------------------------------------------------