UNPKG

cryptocurrency-pool-server

Version:

An extremely efficient, highly scalable, all-in-one, easy to setup cryptocurrency pool server

308 lines (286 loc) 18.6 kB
/*! (c) Andrea Giammarchi - ISC */ var self=this||{};try{!function(t,n){if(new t("q=%2B").get("q")!==n||new t({q:n}).get("q")!==n||new t([["q",n]]).get("q")!==n||"q=%0A"!==new t("q=\n").toString()||"q=+%26"!==new t({q:" &"}).toString())throw t;self.URLSearchParams=t}(URLSearchParams,"+")}catch(t){!function(t,a,o){"use strict";var u=t.create,h=t.defineProperty,n=/[!'\(\)~]|%20|%00/g,e=/\+/g,r={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"},i={append:function(t,n){l(this._ungap,t,n)},delete:function(t){delete this._ungap[t]},get:function(t){return this.has(t)?this._ungap[t][0]:null},getAll:function(t){return this.has(t)?this._ungap[t].slice(0):[]},has:function(t){return t in this._ungap},set:function(t,n){this._ungap[t]=[a(n)]},forEach:function(n,e){var r=this;for(var i in r._ungap)r._ungap[i].forEach(t,i);function t(t){n.call(e,t,a(i),r)}},toJSON:function(){return{}},toString:function(){var t=[];for(var n in this._ungap)for(var e=g(n),r=0,i=this._ungap[n];r<i.length;r++)t.push(e+"="+g(i[r]));return t.join("&")}};for(var s in i)h(c.prototype,s,{configurable:!0,writable:!0,value:i[s]});function c(t){var n=u(null);switch(h(this,"_ungap",{value:n}),!0){case!t:break;case"string"==typeof t:"?"===t.charAt(0)&&(t=t.slice(1));for(var e=t.split("&"),r=0,i=e.length;r<i;r++){var a=(s=e[r]).indexOf("=");-1<a?l(n,p(s.slice(0,a)),p(s.slice(a+1))):s.length&&l(n,p(s),"")}break;case o(t):for(r=0,i=t.length;r<i;r++){var s;l(n,(s=t[r])[0],s[1])}break;case"forEach"in t:t.forEach(f,n);break;default:for(var c in t)l(n,c,t[c])}}function f(t,n){l(this,n,t)}function l(t,n,e){var r=o(e)?e.join(","):e;n in t?t[n].push(r):t[n]=[r]}function p(t){return decodeURIComponent(t.replace(e," "))}function g(t){return encodeURIComponent(t).replace(n,v)}function v(t){return r[t]}self.URLSearchParams=c}(Object,String,Array.isArray)}!function(l){var r=!1;try{r=!!Symbol.iterator}catch(t){}function t(t,n){var e=[];return t.forEach(n,e),r?e[Symbol.iterator]():{next:function(){var t=e.shift();return{done:void 0===t,value:t}}}}"forEach"in l||(l.forEach=function(e,r){var i=this,t=Object.create(null);this.toString().replace(/=[\s\S]*?(?:&|$)/g,"=").split("=").forEach(function(n){!n.length||n in t||(t[n]=i.getAll(n)).forEach(function(t){e.call(r,t,n,i)})})}),"keys"in l||(l.keys=function(){return t(this,function(t,n){this.push(n)})}),"values"in l||(l.values=function(){return t(this,function(t,n){this.push(t)})}),"entries"in l||(l.entries=function(){return t(this,function(t,n){this.push([n,t])})}),!r||Symbol.iterator in l||(l[Symbol.iterator]=l.entries),"sort"in l||(l.sort=function(){for(var t,n,e,r=this.entries(),i=r.next(),a=i.done,s=[],c=Object.create(null);!a;)n=(e=i.value)[0],s.push(n),n in c||(c[n]=[]),c[n].push(e[1]),a=(i=r.next()).done;for(s.sort(),t=0;t<s.length;t++)this.delete(s[t]);for(t=0;t<s.length;t++)n=s[t],this.append(n,c[n].shift())}),function(c){var o=c.defineProperty,u=c.getOwnPropertyDescriptor,h=function(t){var n=t.append;t.append=l.append,URLSearchParams.call(t,t._usp.search.slice(1)),t.append=n},f=function(t,n){if(!(t instanceof n))throw new TypeError("'searchParams' accessed on an object that does not implement interface "+n.name)},t=function(n){var e,r,t=n.prototype,i=u(t,"searchParams"),a=u(t,"href"),s=u(t,"search");!i&&s&&s.set&&(r=function(e){function r(t,n){l.append.call(this,t,n),t=this.toString(),e.set.call(this._usp,t?"?"+t:"")}function i(t){l.delete.call(this,t),t=this.toString(),e.set.call(this._usp,t?"?"+t:"")}function a(t,n){l.set.call(this,t,n),t=this.toString(),e.set.call(this._usp,t?"?"+t:"")}return function(t,n){return t.append=r,t.delete=i,t.set=a,o(t,"_usp",{configurable:!0,writable:!0,value:n})}}(s),e=function(t,n){return o(t,"_searchParams",{configurable:!0,writable:!0,value:r(n,t)}),n},c.defineProperties(t,{href:{get:function(){return a.get.call(this)},set:function(t){var n=this._searchParams;a.set.call(this,t),n&&h(n)}},search:{get:function(){return s.get.call(this)},set:function(t){var n=this._searchParams;s.set.call(this,t),n&&h(n)}},searchParams:{get:function(){return f(this,n),this._searchParams||e(this,new URLSearchParams(this.search.slice(1)))},set:function(t){f(this,n),e(this,t)}}}))};try{t(HTMLAnchorElement),/^function|object$/.test(typeof URL)&&URL.prototype&&t(URL)}catch(t){}}(Object)}(self.URLSearchParams.prototype,Object); $(function(){ var _miner = new URLSearchParams(window.location.search).get('address'); $("#statsWorker").text(_miner); var _workerCount = 0; window.statsSource = new EventSource("/api/live_stats"); var workerHashrateData; var workerSoloHashrateData; var workerHashrateChart; var workerHistoryMax = 160; var statData; var totalHash; var totalSoloHash; var totalImmature; var totalBal; var totalPaid; var totalShares; function getReadableHashRateString(hashrate){ var i = -1; var byteUnits = [ ' KH', ' MH', ' GH', ' TH', ' PH', ' EH', ' ZH', ' YH' ]; do { hashrate = hashrate / 1024; i++; } while (hashrate > 1024); return hashrate.toFixed(2) + byteUnits[i]; } function timeOfDayFormat(timestamp){ var dStr = d3.time.format('%I:%M %p')(new Date(timestamp)); if (dStr.indexOf('0') === 0) dStr = dStr.slice(1); return dStr; } function getWorkerNameFromAddress(w) { var worker = w; if (w.split(".").length > 1) { worker = w.split(".")[1]; if (worker == null || worker.length < 1) { worker = "noname"; } } else { worker = "noname"; } return worker; } function buildChartData(){ var workers = {}; for (var w in statData.history) { var worker = getWorkerNameFromAddress(w); var a = workers[worker] = (workers[worker] || { hashrate: [], solohashrate: [] }); for (var wh in statData.history[w]) { if (statData.history[w][wh].hashrate) { a.hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); } if (statData.history[w][wh].solohashrate) { a.solohashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].solohashrate]); } } var maxValues = Math.max(a.hashrate.length, a.solohashrate.length); if (maxValues > workerHistoryMax) { workerHistoryMax = maxValues; } } var i=0; workerHashrateData = []; for (var worker in workers){ if (workers[worker].hashrate.length) { workerHashrateData.push({ key: worker + ' (PPS)', disabled: (i > Math.min((_workerCount-1), 3)), values: workers[worker].hashrate }); } if (workers[worker].solohashrate.length) { workerHashrateData.push({ key: worker + ' (SOLO)', disabled: (i > Math.min((_workerCount-1), 3)), values: workers[worker].solohashrate }); } i++; } } function updateChartData(){ var workers = {}; for (var w in statData.history) { var worker = getWorkerNameFromAddress(w); // get a reference to lastest workerhistory for (var wh in statData.history[w]) { } var foundPPSWorker = statData.history[w][wh].hashrate === undefined; var foundSOLOWorker = statData.history[w][wh].solohashrate === undefined; for (var i = 0; i < workerHashrateData.length; i++) { if (!foundPPSWorker && workerHashrateData[i].key === (worker + ' (PPS)')) { foundPPSWorker = true; if (workerHashrateData[i].values.length >= workerHistoryMax) { workerHashrateData[i].values.shift(); } workerHashrateData[i].values.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); } if (!foundSOLOWorker && workerHashrateData[i].key === (worker + ' (SOLO)')) { foundSOLOWorker = true; if (workerHashrateData[i].values.length >= workerHistoryMax) { workerHashrateData[i].values.shift(); } workerHashrateData[i].values.push([statData.history[w][wh].time * 1000, statData.history[w][wh].solohashrate]); } if (foundPPSWorker && foundSOLOWorker) { break; } } if (!foundPPSWorker) { var hashrate = []; hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); workerHashrateData.push({ key: worker + ' (PPS)', values: hashrate }); } if (!foundSOLOWorker) { var solohashrate = []; solohashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].solohashrate]); workerHashrateData.push({ key: worker + ' (SOLO)', values: solohashrate }); } if (!foundPPSWorker || !foundSOLOWorker) { rebuildWorkerDisplay(); return true; } } triggerChartUpdates(); return false; } function calculateAverageHashrate(worker, type) { var count = 0; var total = 1; var avg = 0; for (var i = 0; i < workerHashrateData.length; i++) { count = 0; if (worker == null) { for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) { if (workerHashrateData[i].key.indexOf(' (' + type + ')') === (workerHashrateData[i].key.length - (' (' + type + ')').length)) { count++; avg += parseFloat(workerHashrateData[i].values[ii][1]); } } } else { for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) { if (workerHashrateData[i].key === (worker + ' (' + type + ')')) { count++; avg += parseFloat(workerHashrateData[i].values[ii][1]); } } } if (count > total) total = count; } avg = avg / total; return avg; } function triggerChartUpdates(){ workerHashrateChart.update(); } function displayCharts() { nv.addGraph(function() { workerHashrateChart = nv.models.lineChart() .margin({left: 80, right: 30}) .x(function(d){ return d[0] }) .y(function(d){ return d[1] }) .useInteractiveGuideline(true); workerHashrateChart.xAxis.tickFormat(timeOfDayFormat); workerHashrateChart.yAxis.tickFormat(function(d){ return getReadableHashRateString(d); }); d3.select('#workerHashrate').datum(workerHashrateData).call(workerHashrateChart); return workerHashrateChart; }); } function updateStats() { totalHash = statData.totalHash; totalSoloHash = statData.totalSoloHash; totalPaid = statData.paid; totalBal = statData.balance; totalImmature = statData.immature; totalShares = statData.totalShares; // do some calculations var _blocktime = 250; var _networkHashRate = parseFloat(statData.networkSols); var _myHashRate = totalHash; var luckDays = (((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)) || 0).toFixed(3); var _mySoloHashRate = totalSoloHash; var soloLuckDays = (((_networkHashRate / _mySoloHashRate * _blocktime) / (24 * 60 * 60)) || 0).toFixed(3); // update miner stats $("#statsHashrate").text(getReadableHashRateString(totalHash)); $("#statsSoloHashrate").text(getReadableHashRateString(totalSoloHash)); $("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null, 'PPS'))); $("#statsSoloHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null, 'SOLO'))); $("#statsLuckDays").text(luckDays); $("#statsSoloLuckDays").text(soloLuckDays); $("#statsTotalImmature").text(totalImmature); $("#statsTotalBal").text(totalBal); $("#statsTotalPaid").text(totalPaid); $("#statsTotalShares").text(totalShares.toFixed(2)); } function updateWorkerStats() { // update worker stats var i=0; for (var w in statData.workers) { i++; var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, ''); var saneWorkerName = getWorkerNameFromAddress(w); $("#statsHashrate"+htmlSafeWorkerName).text(getReadableHashRateString(statData.workers[w][statData.workers[w].mode === 'SOLO' ? 'solohashrate' : 'hashrate'])); $("#statsHashrateAvg"+htmlSafeWorkerName).text(getReadableHashRateString(calculateAverageHashrate(saneWorkerName, statData.workers[w].mode))); $("#statsLuckDays"+htmlSafeWorkerName).text(statData.workers[w][statData.workers[w].mode === 'SOLO' ? 'soloLuckDays' : 'luckDays']); $("#statsPaid"+htmlSafeWorkerName).text(statData.workers[w].paid); $("#statsBalance"+htmlSafeWorkerName).text(statData.workers[w].balance); $("#statsShares"+htmlSafeWorkerName).text(statData.workers[w].mode === 'SOLO' ? 'SOLO' : Math.round(statData.workers[w].currRoundShares * 100) / 100); $("#statsDiff"+htmlSafeWorkerName).text(statData.workers[w].diff); } } function addWorkerToDisplay(name, htmlSafeName, workerObj) { var htmlToAdd = '<div class="boxStats card" style="margin: 9px; width: 280px; max-width: 100%">'; if (htmlSafeName.indexOf("_") >= 0) { htmlToAdd+= '<div class="boxLowerHeader">'+htmlSafeName.substr(htmlSafeName.indexOf("_")+1,htmlSafeName.length)+'</div>'; } else { htmlToAdd+= '<div class="boxLowerHeader">noname</div>'; } htmlToAdd+='<div class="boxStatsList"><div><div class="poolStats"><div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path d="M22 9V7h-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2v-2h-2V9h2zm-4 10H4V5h14v14zM6 13h5v4H6zm6-6h4v3h-4zM6 7h5v5H6zm6 4h4v6h-4z"/><path fill="none" d="M0 0h24v24H0zm0 0h24v24H0z"/></svg>&nbsp;&nbsp;<span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj[workerObj.mode === 'SOLO' ? 'solohashrate' : 'hashrate'])+'</span>&nbsp;(Now)</div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path d="M22 9V7h-2V5c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-2h2v-2h-2v-2h2v-2h-2V9h2zm-4 10H4V5h14v14zM6 13h5v4H6zm6-6h4v3h-4zM6 7h5v5H6zm6 4h4v6h-4z"/><path fill="none" d="M0 0h24v24H0zm0 0h24v24H0z"/></svg>&nbsp;&nbsp;<span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name, workerObj.mode))+'</span>&nbsp;(Average)</div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24" class="material"><defs><path id="a" d="M0 0h24v24H0V0z"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path d="M1 21h12v2H1zM5.245 8.07l2.83-2.827 14.14 14.142-2.828 2.828zM12.317 1l5.657 5.656-2.83 2.83-5.654-5.66zM3.825 9.485l5.657 5.657-2.828 2.828-5.657-5.657z" clip-path="url(#b)"/></svg>&nbsp;&nbsp;Luck&nbsp;<span id="statsLuckDays'+htmlSafeName+'">'+workerObj[workerObj.mode === 'SOLO' ? 'soloLuckDays' : 'luckDays']+'</span>&nbsp;Days</div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 10.99h7c-.53 4.12-3.28 7.79-7 8.94V12H5V6.3l7-3.11v8.8z"/><path d="M0 0h24v24H0z" fill="none"/></svg>&nbsp;&nbsp;Difficulty:&nbsp;<span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>&nbsp;&nbsp;Shares:&nbsp;<span id="statsShares'+htmlSafeName+'">'+(workerObj.mode === 'SOLO' ? 'SOLO' : Math.round(workerObj.currRoundShares * 100) / 100)+'</span></div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M5 8h2v8H5zm7 0H9c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 6h-1v-4h1v4zm7-6h-3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 6h-1v-4h1v4z"/><path fill="none" d="M4 6h16v12H4z"/><path d="M2 4v16h20V4H2zm2 14V6h16v12H4z"/></svg>&nbsp;&nbsp;Balance:&nbsp;<span id="statsBalance'+htmlSafeName+'">'+workerObj.balance+'</span></div>'; htmlToAdd+='<div class="poolStat"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" class="material"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M5 8h2v8H5zm7 0H9c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 6h-1v-4h1v4zm7-6h-3c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1h3c.55 0 1-.45 1-1V9c0-.55-.45-1-1-1zm-1 6h-1v-4h1v4z"/><path fill="none" d="M4 6h16v12H4z"/><path d="M2 4v16h20V4H2zm2 14V6h16v12H4z"/></svg>&nbsp;&nbsp;Paid:&nbsp;<span id="statsPaid'+htmlSafeName+'">'+workerObj.paid+'</span></div></div></div></div></div>'; $("#boxesWorkers").html($("#boxesWorkers").html()+htmlToAdd); } function rebuildWorkerDisplay() { $("#boxesWorkers").html(""); var i=0; for (var w in statData.workers) { i++; var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, ''); var saneWorkerName = getWorkerNameFromAddress(w); addWorkerToDisplay(saneWorkerName, htmlSafeWorkerName, statData.workers[w]); } } // resize chart on window resize nv.utils.windowResize(triggerChartUpdates); // grab initial stats $.getJSON('/api/worker_stats?'+_miner, function(data){ statData = data; for (var w in statData.workers) { _workerCount++; } buildChartData(); displayCharts(); rebuildWorkerDisplay(); updateStats(); }); // live stat updates statsSource.addEventListener('message', function(e){ // TODO, create miner_live_stats... // miner_live_stats will return the same josn except without the worker history // FOR NOW, use this to grab updated stats $.getJSON('/api/worker_stats?'+_miner, function(data){ statData = data; // check for missing workers var wc = 0; var rebuilt = false; // update worker stats for (var w in statData.workers) { wc++; } // TODO, this isn't 100% fool proof! if (_workerCount != wc) { if (_workerCount > wc) { rebuildWorkerDisplay(); rebuilt = true; } _workerCount = wc; } rebuilt = (rebuilt || updateChartData()); updateStats(); if (!rebuilt) { updateWorkerStats(); } }); }); });