@ -1,12 +1,12 @@ |
|||
{ |
|||
"name": "hush", |
|||
"symbol": "hush", |
|||
"algorithm": "equihash", |
|||
"peerMagic": "f9eee48d", |
|||
"txfee": 0.0001, |
|||
"privateChain": true, |
|||
"sapling": true, |
|||
"overwintered": true, |
|||
"requireShielding": true, |
|||
"disablecb": true |
|||
"name": "hush", |
|||
"symbol": "hush", |
|||
"algorithm": "equihash", |
|||
"peerMagic": "f9eee48d", |
|||
"txfee": 0.0001, |
|||
"privateChain": true, |
|||
"sapling": true, |
|||
"overwintered": true, |
|||
"requireShielding": true, |
|||
"disablecb": true |
|||
} |
@ -0,0 +1,9 @@ |
|||
{ |
|||
"name": "WSB", |
|||
"symbol": "WSB", |
|||
"algorithm": "equihash", |
|||
"txfee": 0.0001, |
|||
"peerMagic": "faa04bca", |
|||
"sapling": true, |
|||
"disablecb": true |
|||
} |
@ -0,0 +1,38 @@ |
|||
{ |
|||
"enabled": false, |
|||
"coin": "wsb.json", |
|||
"address": "RWEBo1Yp4uGkeXPi1ZGQARfLPkGmoW1MwY", |
|||
"zAddress": "", |
|||
"tAddress": "RWEBo1Yp4uGkeXPi1ZGQARfLPkGmoW1MwY", |
|||
"walletInterval": 1, |
|||
"rewardRecipients": {}, |
|||
"tlsOptions": { "enabled": false }, |
|||
"paymentProcessing": { "enabled": false, "daemon": false }, |
|||
"trackShares": { "disable": true }, |
|||
"ports": { |
|||
"3333": { |
|||
"tls":false, |
|||
"diff": 0.001, |
|||
"varDiff": { |
|||
"minDiff": 0.001, |
|||
"maxDiff": 10000, |
|||
"targetTime": 15, |
|||
"retargetTime": 60, |
|||
"variancePercent": 30 |
|||
} |
|||
} |
|||
}, |
|||
"daemons": [{ |
|||
"host": "127.0.0.1", |
|||
"port": 52043, |
|||
"user": "user", |
|||
"password": "pass" |
|||
}], |
|||
"p2p": { |
|||
"enabled": true, |
|||
"host": "127.0.0.1", |
|||
"port": 52042, |
|||
"disableTransactions": true |
|||
}, |
|||
"mposMode": { "enabled": false } |
|||
} |
@ -0,0 +1,86 @@ |
|||
<!doctype html> |
|||
|
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<link rel="shortcut icon" type="image/png" href="/static/hushfavicon.ico"/> |
|||
|
|||
<link href='http://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'> |
|||
|
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css"> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css"> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/0.4.2/pure-min.css"> |
|||
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script> |
|||
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script> |
|||
<script src="/static/nvd3.js"></script> |
|||
<link rel="stylesheet" href="/static/nvd3.css"> |
|||
|
|||
<script src="/static/main.js"></script> |
|||
<link rel="stylesheet" href="/static/style.css"> |
|||
|
|||
<title>hnomp</title> |
|||
|
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<header> |
|||
<div class="home-menu pure-menu pure-menu-open pure-menu-horizontal"> |
|||
|
|||
<a class="pure-menu-heading hot-swapper" href="/"><i class="fa fa-home"></i> Home</a> |
|||
|
|||
<ul> |
|||
<li class="{{? it.selected === 'getting_started' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/getting_started"> |
|||
<i class="fa fa-rocket"></i> |
|||
Getting Started |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'stats' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/stats"> |
|||
<i class="fa fa-bar-chart-o"></i> |
|||
Graph Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'tbs' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/tbs"> |
|||
<i class="fa fa-table"></i> |
|||
Tab Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'workers' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/workers"> |
|||
<i class="fa fa-cogs"></i> |
|||
Workers Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'payments' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/payments"> |
|||
<i class="fa fa-bitcoin"></i> |
|||
Payments |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'api' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/api"> |
|||
<i class="fa fa-code"></i> |
|||
API |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</header> |
|||
|
|||
<main> |
|||
{{=it.page}} |
|||
</main> |
|||
|
|||
|
|||
<footer> |
|||
<3 🐸 · <a href="https://git.hush.is/hush/hnomp" target="_blank">hnomp on our own Gitea</a> |
|||
</footer> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,50 @@ |
|||
<div> |
|||
|
|||
<style> |
|||
#passwordForm, #adminCenter{ |
|||
display: none; |
|||
} |
|||
#adminCenter{ |
|||
display: flex; |
|||
flex-flow: row; |
|||
} |
|||
#leftMenu{ |
|||
flex: 0 0 200px; |
|||
} |
|||
#editForm{ |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
|
|||
<form class="pure-form pure-form-stacked" id="passwordForm"> |
|||
<fieldset> |
|||
<legend>Password</legend> |
|||
|
|||
<input id="password" type="password" placeholder="Password"> |
|||
|
|||
<label for="remember" class="pure-checkbox"> |
|||
<input id="remember" type="checkbox"> Stay Logged In |
|||
</label> |
|||
|
|||
<button type="submit" class="pure-button pure-button-primary">Log In</button> |
|||
</fieldset> |
|||
</form> |
|||
|
|||
<div id="adminCenter"> |
|||
|
|||
<div class="pure-menu pure-menu-open" id="leftMenu"> |
|||
<a class="pure-menu-heading">Administration</a> |
|||
<ul> |
|||
<li id="addPool"><a href="#">Add Pool</a></li> |
|||
<li class="pure-menu-heading" id="poolList">Current Pools</li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div id="editForm"></div> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<script src="/static/admin.js"></script> |
|||
|
|||
</div> |
@ -0,0 +1,11 @@ |
|||
<div style="margin: 18px;"> |
|||
API - The API is work in progress and is subject to change during development. |
|||
<ul> |
|||
<li><a href="/api/stats">/api/stats</a> global pool stats</li> |
|||
<li><a href="/api/blocks">/api/blocks</a> global block stats</li> |
|||
<li><a href="/api/pool_stats">/api/pool_stats</a> - historical stats</li> |
|||
<li><a href="/api/payments">/api/payments</a> - payment history</li> |
|||
<li><a href="/api/worker_stats?taddr">/api/worker_stats?taddr</a> - historical time per pool json </li> |
|||
<li><a href="/api/live_stats">/api/live_stats</a> - live stats (websocket)</li> |
|||
</ul> |
|||
</div> |
@ -0,0 +1,325 @@ |
|||
<style> |
|||
#holder{ |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
|
|||
.glow{ |
|||
box-shadow: inset 0 0 12px 4px #ff6c00; |
|||
} |
|||
|
|||
.hidden{ |
|||
display: none !important; |
|||
} |
|||
|
|||
#menu{ |
|||
background-color: #3d3d3d; |
|||
min-width: 170px; |
|||
} |
|||
|
|||
#menu > .menuHeader{ |
|||
color: #e3f7ff; |
|||
border-bottom: 1px solid #7f878b; |
|||
font-size: 1.2em; |
|||
padding: 16px 16px 4px 15px; |
|||
} |
|||
|
|||
.menuList{ |
|||
transition-duration: 200ms; |
|||
} |
|||
|
|||
.menuList > a:first-child{ |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.menuList > a{ |
|||
display: inline-block; |
|||
color: #e3f7ff; |
|||
text-decoration: none; |
|||
padding: 7px; |
|||
padding-left: 25px; |
|||
width:40px; |
|||
} |
|||
|
|||
.menuList > a:hover{ |
|||
color: #f69b3a; |
|||
} |
|||
|
|||
#main{ |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: column; |
|||
margin: 18px; |
|||
} |
|||
.miningOption{ |
|||
color: white; |
|||
/* display: flex; |
|||
flex: 1 1 auto; |
|||
flex-direction: row; |
|||
flex-wrap: wrap; */ |
|||
min-height: 215px; |
|||
justify-content: center; |
|||
align-items: center; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
a.miningOption:hover{ |
|||
color: #f69b3a; |
|||
} |
|||
|
|||
.miningOption:first-child{ |
|||
background-color: #0eafc7; |
|||
} |
|||
.miningOption:last-child{ |
|||
background-color: #b064e1; |
|||
} |
|||
.miningOptionNum{ |
|||
font-size: 6em; |
|||
padding-right: 20px; |
|||
width: 140px; |
|||
text-align: center; |
|||
} |
|||
.miningOptionInstructions{ |
|||
flex: 1 1 auto; |
|||
padding:10px; |
|||
} |
|||
|
|||
.miningOptionInstructions > div:first-child{ |
|||
font-size: 2.4em; |
|||
} |
|||
.miningOptionInstructions > div:last-child{ |
|||
margin-top: 20px; |
|||
font-size: 1.3em; |
|||
} |
|||
|
|||
#orHolder{ |
|||
height: 37px; |
|||
text-align: center; |
|||
} |
|||
#orLine{ |
|||
border-bottom: 1px solid #c2cacf; |
|||
height: 19px; |
|||
margin-bottom: -13px; |
|||
} |
|||
#orText{ |
|||
background-color: #ebf4fa; |
|||
color: #5c5c5c; |
|||
display: inline-block; |
|||
width: 35px; |
|||
font-style: italic; |
|||
} |
|||
|
|||
#coinInfoBackground{ |
|||
transition-duration: 400ms; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: black; |
|||
opacity: 0.0; |
|||
} |
|||
#coinInfo{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
color: white; |
|||
width: 750px; |
|||
min-height: 400px; |
|||
top: 50px; |
|||
left: 50%; |
|||
margin-left: -375px; |
|||
position: absolute; |
|||
background-color: #f06350; |
|||
} |
|||
#coinInfo .coinInfoName{ |
|||
text-transform: capitalize; |
|||
} |
|||
#coinInfo > div:first-of-type{ |
|||
font-size: 1.8em; |
|||
text-align: center; |
|||
margin-top: 40px; |
|||
margin-bottom: 35px; |
|||
} |
|||
#coinInfoRows{ |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
flex: 1 1 auto; |
|||
margin-bottom: 70px; |
|||
} |
|||
#coinInfoRows > div{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
} |
|||
#coinInfoRows > div > div{ |
|||
padding: 3px; |
|||
} |
|||
#coinInfoRowKeys{ |
|||
font-weight: bold; |
|||
padding-right: 30px; |
|||
} |
|||
#coinInfoRowKeys .coinInfoSubtle{ |
|||
font-weight: normal; |
|||
} |
|||
#coinInfoRowValues{ |
|||
|
|||
} |
|||
#coinInfoClose{ |
|||
position: absolute; |
|||
font-size: 3em; |
|||
top: 0; |
|||
right: 0; |
|||
width: 60px; |
|||
height: 60px; |
|||
text-align: center; |
|||
color: white; |
|||
text-decoration: none; |
|||
} |
|||
#coinInfoClose:hover{ |
|||
color: #50f0e3; |
|||
} |
|||
</style> |
|||
|
|||
<div id="holder"> |
|||
<a href="#" class="miningOption" id="coinGlowTrigger"> |
|||
<div class="miningOptionInstructions"> |
|||
<div>Select a coin for connection details</div> |
|||
<div>Configurations for each coin are available for advanced miners</div> |
|||
</div> |
|||
</a> |
|||
|
|||
<div id="menu"> |
|||
|
|||
{{? (function(){ |
|||
if (!it.portalConfig.switching) return false; |
|||
for (var p in it.portalConfig.switching){ |
|||
if (it.portalConfig.switching[p].enabled) |
|||
return true; |
|||
} |
|||
return false; |
|||
})() |
|||
}} |
|||
<div class="menuHeader">Coin-Switching Ports</div> |
|||
{{?}} |
|||
|
|||
<div class="menuList"> |
|||
{{ for (var p in it.portalConfig.switching){ |
|||
if (!it.portalConfig.switching[p].enabled) continue; |
|||
var info = { |
|||
algo: p, |
|||
ports: {}, |
|||
host: it.portalConfig.website.stratumHost |
|||
}; |
|||
info.ports[it.portalConfig.switching[p].port] = {diff: it.portalConfig.switching[p].diff}; |
|||
info = JSON.stringify(info).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=p}}</a> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
<div class="menuHeader">Coins</div> |
|||
<div class="menuList" id="coinList"> |
|||
{{ for(var pool in it.poolsConfigs) { |
|||
var info = JSON.stringify({ |
|||
coin: it.poolsConfigs[pool].coin, |
|||
algo: it.poolsConfigs[pool].coin.algorithm, |
|||
ports: it.poolsConfigs[pool].ports, |
|||
host: it.portalConfig.website.stratumHost |
|||
}).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=pool}} {{=Object.keys(it.poolsConfigs[pool].ports)[0]}}</a> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- <div id="main"> |
|||
<a href="#" class="miningOption" id="nompAppDownload"> |
|||
<div class="miningOptionNum">1.</div> |
|||
<div class="miningOptionInstructions"> |
|||
<div>Download NOMP App</div> |
|||
<div>Our preconfigured app makes mining that easy</div> |
|||
</div> |
|||
</a> |
|||
<div id="orHolder"> |
|||
<div id="orLine"></div> |
|||
<div id="orText">or</div> |
|||
</div> |
|||
<a href="#" class="miningOption" id="coinGlowTrigger"> |
|||
<div class="miningOptionNum">2.</div> |
|||
<div class="miningOptionInstructions"> |
|||
<div>Select a coin for connection details</div> |
|||
<div>Configurations for each coin are available for advanced miners</div> |
|||
</div> |
|||
</a> |
|||
</div> --> |
|||
</div> |
|||
|
|||
<a href="#" id="coinInfoBackground" class="hidden"></a> |
|||
|
|||
<div id="coinInfo" class="hidden"> |
|||
<a href="#" id="coinInfoClose">×</a> |
|||
<div><span class="coinInfoName"></span> Configuration:</div> |
|||
<div id="coinInfoRows"> |
|||
<div id="coinInfoRowKeys"> |
|||
<div>Username:</div> |
|||
<div>Password:</div> |
|||
</div> |
|||
<div id="coinInfoRowValues"> |
|||
<div id="coinInfoUsername"></div> |
|||
<div>anything</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script> |
|||
|
|||
function showCoinConfig(info){ |
|||
|
|||
var htmlKeys = '<div class="coinInfoData">Algorithm:</div>'; |
|||
var htmlValues = '<div class="coinInfoData">' + info.algo + '</div>'; |
|||
|
|||
for (var port in info.ports){ |
|||
htmlKeys += '<div class="coinInfoData">URL <span class="coinInfoSubtle">(difficulty ' + info.ports[port].diff + ')</span>:</div>'; |
|||
htmlValues += '<div class="coinInfoData">stratum+tcp://' + info.host + ':' + port + '</div>'; |
|||
} |
|||
|
|||
if (info.coin) |
|||
$('#coinInfoUsername').text('your ' + info.coin.name + ' wallet address'); |
|||
else |
|||
$('#coinInfoUsername').text('your public key'); |
|||
$('.coinInfoData').remove(); |
|||
$('#coinInfoRowKeys').append(htmlKeys); |
|||
$('#coinInfoRowValues').append(htmlValues); |
|||
} |
|||
|
|||
$('#coinGlowTrigger').click(function(event){ |
|||
event.preventDefault(); |
|||
$('.menuList').addClass('glow'); |
|||
setTimeout(function(){ |
|||
$('.menuList').removeClass('glow'); |
|||
}, 200); |
|||
return false; |
|||
}); |
|||
|
|||
$('.poolOption').click(function(event){ |
|||
event.preventDefault(); |
|||
showCoinConfig($(this).data('info')); |
|||
$('#coinInfoBackground,#coinInfo').removeClass('hidden'); |
|||
$('#coinInfoBackground').css('opacity', 0.7); |
|||
return false; |
|||
}); |
|||
|
|||
$('#coinInfoBackground,#coinInfoClose').click(function(event){ |
|||
event.preventDefault(); |
|||
$('#coinInfoBackground,#coinInfo').addClass('hidden'); |
|||
$('#coinInfoBackground').css('opacity', 0.0); |
|||
return false; |
|||
}); |
|||
|
|||
$('#nompAppDownload').click(function(event){ |
|||
event.preventDefault(); |
|||
alert('NOMP App development still in progress...'); |
|||
return false; |
|||
}); |
|||
</script> |
@ -0,0 +1,132 @@ |
|||
<style> |
|||
#boxWelcome{ |
|||
background-color: #0eafc7; |
|||
color: white; |
|||
margin: 18px; |
|||
} |
|||
#logoImg{ |
|||
height: 285px; |
|||
margin: 55px; |
|||
} |
|||
#welcomeText{ |
|||
font-size: 2.7em; |
|||
margin: 50px 18px 10px 18px; |
|||
} |
|||
#welcomeItems{ |
|||
list-style-type: none; |
|||
font-size: 1.3em; |
|||
padding: 0 !important; |
|||
margin: 0 0 0 18px !important; |
|||
} |
|||
#welcomeItems > li{ |
|||
margin: 30px !important; |
|||
} |
|||
#boxesLower { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesLower > div { |
|||
display: flex; |
|||
} |
|||
#boxesLower > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
|
|||
|
|||
#boxStatsLeft{ |
|||
background-color: #b064e1; |
|||
} |
|||
#boxStatsRight{ |
|||
background-color: #10bb9c; |
|||
} |
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
/* align-content: center; */ |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
|||
|
|||
|
|||
</style> |
|||
|
|||
<div class="pure-g-r" id="boxWelcome"> |
|||
<img src="/static/hush-logo-horizontal-01.png" style="margin: 0 auto; height:150px;"> |
|||
|
|||
<div class="pure-u-1"> |
|||
<div style="font-size: 2em; text-align:center; margin-bottom:10px;">HNOMP</div> |
|||
<div style="text-align:center; margin-bottom:10px;"">Low 1.5% fee! Min payouts of 1!</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-g-r" id="boxesLower"> |
|||
<div class="pure-u-1"> |
|||
<div class="boxStats" id="boxStatsLeft"> |
|||
<div class="boxLowerHeader">Global Stats</div> |
|||
<div class="boxStatsList"> |
|||
{{ for(var algo in it.stats.algos) { }} |
|||
<div> |
|||
<div><i class="fa fa-flask"></i>{{=algo}}</div> |
|||
<div><i class="fa fa-users"></i><span id="statsMiners{{=algo}}">{{=it.stats.algos[algo].workers}}</span> Miners</div> |
|||
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=algo}}">{{=it.stats.algos[algo].hashrateString}}</span></div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-u-1"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">Pools / Coins</div> |
|||
<div class="boxStatsList"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div> |
|||
<div><i class="fa fa-dot-circle-o"></i>{{=pool}}</div> |
|||
<div><i class="fa fa-users"></i><span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Miners</div> |
|||
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span></div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<script> |
|||
$(function() { |
|||
statsSource.addEventListener('message', function (e) { |
|||
var stats = JSON.parse(e.data); |
|||
for (algo in stats.algos) { |
|||
$('#statsMiners' + algo).text(stats.algos[algo].workers); |
|||
$('#statsHashrate' + algo).text(stats.algos[algo].hashrateString); |
|||
} |
|||
for (var pool in stats.pools) { |
|||
$('#statsMiners' + pool).text(stats.pools[pool].workerCount); |
|||
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString); |
|||
} |
|||
}); |
|||
}); |
|||
</script> |
@ -0,0 +1,105 @@ |
|||
<style> |
|||
#topCharts{ |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-top: 18px; |
|||
padding-bottom: 0px; |
|||
} |
|||
#topCharts > div > div > svg{ |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.chartWrapper{ |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.chartLabel{ |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
.chartHolder{ |
|||
|
|||
} |
|||
|
|||
#boxesWorkers { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesWorkers > div { |
|||
display: flex; |
|||
} |
|||
#boxesWorkers > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
|
|||
#boxStatsLeft{ |
|||
color: black; |
|||
background-color: #cccccc; |
|||
} |
|||
#boxStatsRight{ |
|||
color: black; |
|||
background-color: #cccccc; |
|||
} |
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
align-content: center; |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
|||
|
|||
</style> |
|||
|
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<div class="chartLabel"> |
|||
<!--<div style="float:left; padding-right: 18px;"><i class="fa fa-users"></i><span id="statsWorkers">...</span></div>--> |
|||
<div style="float:left; margin-right: 9px;">{{=String(it.stats.address).split(".")[0]}}</div> |
|||
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg">...</span> (Avg)</small></div> |
|||
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-tachometer"></i> <span id="statsHashrate">...</span> (Now)</small></div> |
|||
<div style="float:right; padding-left: 18px;"><small><i class="fa fa-gavel"></i> Luck <span id="statsLuckDays">...</span> Days</small></div> |
|||
</div> |
|||
<div class="chartHolder"><svg id="workerHashrate" /></div> |
|||
<div> |
|||
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fa fa-cog"></i> Shares: <span id="statsTotalShares">...</span></div> |
|||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Immature: <span id="statsTotalImmature">...</span> </div> |
|||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fa fa-money"></i> Bal: <span id="statsTotalBal">...</span> </div> |
|||
<div style="padding-top: 9px; padding-left: 18px;"><i class="fa fa-money"></i> Paid: <span id="statsTotalPaid">...</span> </div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="boxesWorkers"> </div> |
|||
|
|||
<script> |
|||
var _miner = "{{=String(it.stats.address).split(".")[0]}}"; |
|||
var _workerCount = 0; |
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/miner_stats.js'; |
|||
</script> |
@ -0,0 +1,25 @@ |
|||
<style> |
|||
#miningKeyPage{ |
|||
margin: 15px; |
|||
} |
|||
#keyFrame{ |
|||
padding: 0; |
|||
border: 0; |
|||
width: 100%; |
|||
height: 750px; |
|||
display: block; |
|||
} |
|||
</style> |
|||
|
|||
<div id="miningKeyPage"> |
|||
|
|||
<p> |
|||
This script run client-side (in your browser). For maximum security <a href="/key.html" download="key.html">download</a> the script and run it locally and |
|||
offline in a modern web browser. |
|||
</p> |
|||
|
|||
<iframe id="keyFrame" src="/key.html"> |
|||
|
|||
</iframe> |
|||
|
|||
</div> |
@ -0,0 +1,92 @@ |
|||
<style> |
|||
#bottomNotes { |
|||
display: block; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-bottom: 18px; |
|||
} |
|||
#topPool { |
|||
padding-top: 18px; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
} |
|||
#topPool > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.poolWrapper { |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.poolLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
.poolMinerTable { |
|||
|
|||
} |
|||
table { |
|||
width: 100%; |
|||
} |
|||
table td { |
|||
padding: 6px 12px; |
|||
} |
|||
</style> |
|||
<script type="text/javascript"> |
|||
$(function () { |
|||
$(document).tooltip({ |
|||
content: function () { |
|||
return $(this).prop('title'); |
|||
}, |
|||
show: null, |
|||
close: function (event, ui) { |
|||
ui.tooltip.hover( |
|||
|
|||
function () { |
|||
$(this).stop(true).fadeTo(400, 1); |
|||
}, |
|||
|
|||
function () { |
|||
$(this).fadeOut("400", function () { |
|||
$(this).remove(); |
|||
}) |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
</script> |
|||
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }} |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<table class="puretable"> |
|||
<thead> |
|||
<tr> |
|||
<th>Blocks</th> |
|||
<th>Time</th> |
|||
<th>Miners</th> |
|||
<th>Shares</th> |
|||
<th>Amount</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var p in it.stats.pools[pool].payments) { }} |
|||
<tr> |
|||
<td style="max-width: 475px; word-wrap: break-word;"> |
|||
{{if (String(it.stats.pools[pool].name) == 'kmd') { }} |
|||
<a href="https://kmdexplorer.io/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name) == 'kmdice') { }} |
|||
<a href="http://kmdice.explorer.dexstats.info/tx/{{=it.stats.pools[pool].payments[p].txid}}" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else { }} |
|||
{{=it.stats.pools[pool].payments[p].blocks}} |
|||
{{ } }} |
|||
</td> |
|||
<td>{{=readableDate(it.stats.pools[pool].payments[p].time)}}</td> |
|||
<td>{{=it.stats.pools[pool].payments[p].miners}}</td> |
|||
<td>{{=Math.round(it.stats.pools[pool].payments[p].shares)}}</td> |
|||
<td>{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
|||
</div> |
|||
{{ } }} |
@ -0,0 +1,356 @@ |
|||
<style> |
|||
#topCharts{ |
|||
padding: 18px; |
|||
} |
|||
#topCharts > div > div > svg{ |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.chartWrapper{ |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.chartLabel{ |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
#boxesLower { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesLower > div { |
|||
display: flex; |
|||
} |
|||
#boxesLower > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
#boxStatsLeft{ |
|||
color: black; |
|||
background-color: #cccccc; |
|||
} |
|||
#boxStatsRight{ |
|||
color: black; |
|||
background-color: #cccccc; |
|||
} |
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
align-content: center; |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
|||
|
|||
div.tooltip { |
|||
position: absolute; |
|||
text-align: center; |
|||
width: 60px; |
|||
height: 28px; |
|||
padding: 2px; |
|||
font: 12px sans-serif; |
|||
background: lightsteelblue; |
|||
border: 0px; |
|||
border-radius: 8px; |
|||
pointer-events: none; |
|||
} |
|||
#tooltip.hidden { |
|||
opacity: 0; |
|||
} |
|||
</style> |
|||
|
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<div class="chartLabel">Pool Historical Hashrate</div> |
|||
<div class="chartHolder"><svg id="poolHashrate"/></div> |
|||
</div> |
|||
</div> |
|||
|
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }} |
|||
|
|||
<div class="pure-g-r" id="boxesLower"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsLeft"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Pool Stats</div> |
|||
<div class="boxStatsList"> |
|||
<div> |
|||
<div><i class="fa fa-users"></i><span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</div> |
|||
<div><i class="fa fa-rocket"></i><span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</div> |
|||
<div><i class="fa fa-tachometer"></i><span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</div> |
|||
<div><i class="fa fa-tachometer"></i><span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</div> |
|||
<div><i class="fa fa-gavel"></i>Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Network Stats</div> |
|||
<div class="boxStatsList"> |
|||
<div> |
|||
<div><i class="fa fa-bars" aria-hidden="true"></i><small>Block Height:</small> <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></div> |
|||
<div><i class="fa fa-tachometer"></i><small>Network Hash/s:</small> <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></div> |
|||
<div><i class="fa fa-unlock-alt" aria-hidden="true"></i><small>Difficulty:</small> <span id="statsNetworkDiff{{=pool}}">{{=it.stats.pools[pool].poolStats.networkDiff}}</span></div> |
|||
<div><i class="fa fa-users"></i><small>Node Connections:</small> <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
{{ for(var pool in it.stats.pools) { }} |
|||
{{ var blockscomb = new Array; }} |
|||
<div class="pure-g-r" id="boxesLower"> |
|||
<div class="pure-u-1-1"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Blocks Found |
|||
<span style="float:right;"><small> |
|||
<i class="fa fa-bars"></i> <span id="statsValidBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.validBlocks}}</span> Blocks |
|||
<i class="fa fa-money"></i> Paid: <span id="statsTotalPaid{{=pool}}">{{=(parseFloat(it.stats.pools[pool].poolStats.totalPaid)).toFixed(8)}}</span> {{=it.stats.pools[pool].symbol}}</small> </span> |
|||
</div> |
|||
|
|||
<div class="boxStatsList" style="margin-top: 9px;"> |
|||
<!--<div id="{{=it.stats.pools[pool].name}}NewBlocks"></div>--> |
|||
{{ for(var b in it.stats.pools[pool].pending.blocks) { }} |
|||
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }} |
|||
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;" title="{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }}{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }}Waiting for dPoW notarization{{} else if (it.stats.pools[pool].pending.confirms[block[0]] < it.poolsConfigs[pool].paymentProcessing.minConf*2) { }}Waiting for min confirmations{{ } else { }}Queued for payment{{ } }}{{ } else { }}Waiting for payment processor to review{{ } }}"> |
|||
<i class="fa fa-bars"></i> |
|||
<small>Block:</small> |
|||
{{if (String(it.stats.pools[pool].name) == "kmd") { }} |
|||
<a href="https://kmdexplorer.io/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name) == "rkt") { }} |
|||
<a href="http://rkt.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name) == "kmdice") { }} |
|||
<a href="http://kmdice.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else { }} |
|||
{{=block[2]}} |
|||
{{ } }} |
|||
|
|||
{{if (block[4] != null) { }} |
|||
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }} |
|||
{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }} |
|||
<span style="float:right; color: red;"><small>Waiting for Notarization</small></span> |
|||
{{ } else { }} |
|||
<span style="float:right; color: red;"><small>{{=it.stats.pools[pool].pending.confirms[block[0]]}} of {{=it.poolsConfigs[pool].paymentProcessing.minConf*2}} Confirmations</small></span> |
|||
{{ } }} |
|||
{{ } else { }} |
|||
<span style="float:right; color: red;"><small>*PENDING*</small></span> |
|||
{{ } }} |
|||
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div> |
|||
</div> |
|||
{{ blockscomb.push(block);}} |
|||
{{ } }} |
|||
|
|||
{{ var i=0; for(var b in it.stats.pools[pool].confirmed.blocks) { }} |
|||
{{ if (i < 8) { i++; }} |
|||
{{ var block = it.stats.pools[pool].confirmed.blocks[b].split(":"); }} |
|||
<div style="margin-bottom: 9px; background-color: #eeeeee; min-width:600px;"><i class="fa fa-bars"></i> |
|||
<small>Block:</small> |
|||
{{if (String(it.stats.pools[pool].name) == "kmd") { }} |
|||
<a href="https://kmdexplorer.io/blocks/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name).startsWith("rkt")) { }} |
|||
<a href="http://rkt.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name).startsWith("kmdice")) { }} |
|||
<a href="http://kmdice.explorer.dexstats.info/block/{{=block[0]}}" target="_blank">{{=block[2]}}</a> |
|||
{{ } else { }} |
|||
{{=block[2]}} |
|||
{{ } }} |
|||
{{if (block[4] != null) { }} |
|||
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
<span style="float:right; padding-left: 18px; color: green;"><small>*PAID*</small></span> |
|||
<div><i class="fa fa-gavel"></i><small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div> |
|||
</div> |
|||
{{blockscomb.push(block);}} |
|||
{{ } }} |
|||
{{ } }} |
|||
|
|||
{{if (blockscomb.length > 0) { }} |
|||
<!--{{=JSON.stringify(blockscomb)}}--> |
|||
<script> |
|||
var blockscomb = ({{=JSON.stringify(blockscomb)}}) |
|||
</script> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{{if (blockscomb.length > 0) { }} |
|||
<center><div id="bottomCharts{{=pool}}" style="text-align:center;" align="center"> |
|||
<div class="chartWrapper" style="text-align:center;"> |
|||
<div class="chartLabel">Finders of the last {{=blockscomb.length}} blocks</div> |
|||
<div class="hidden" id="tooltip{{=pool}}"><p><span id="value{{=pool}}"></span> blocks found by <span id="finderr{{=pool}}"></span></p></div> |
|||
<div class="chartHolder" id="pie{{=pool}}"><svg id="blocksPie{{=pool}}" style="display: block; margin: auto; text-align:center;"/></div> |
|||
</div> |
|||
</div></center> |
|||
|
|||
<script> |
|||
|
|||
var groupedByFinder = {}; |
|||
var data = []; |
|||
|
|||
for (var i=0; i < blockscomb.length; i++) { |
|||
finder=blockscomb[i][3]; // if other doesn 't already have a property for the current letter |
|||
// create it and assign it to a new empty array |
|||
if (!(finder in groupedByFinder)) |
|||
groupedByFinder[finder] = []; |
|||
|
|||
groupedByFinder[finder].push(blockscomb[i]); |
|||
} |
|||
|
|||
Object.keys(groupedByFinder).forEach(function(i) { |
|||
var obj = {}; |
|||
obj.label = i |
|||
obj.value = groupedByFinder[i].length |
|||
data.push(obj) |
|||
}); |
|||
|
|||
console.log(JSON.stringify(data)) |
|||
|
|||
var w = 400; |
|||
var h = 400; |
|||
var r = h/2; |
|||
var legendRectSize = 18; |
|||
var legendSpacing = 5; |
|||
|
|||
var color = d3.scale.category20c(); |
|||
var div = d3.select("#pie{{=pool}}").append("div") |
|||
.attr("class", "tooltip") |
|||
.style("opacity", 0); |
|||
|
|||
var vis = d3.select('#blocksPie{{=pool}}') |
|||
.data([data]) |
|||
.attr("width", 1000) |
|||
.attr("height", h) |
|||
.attr("style", "display: block; margin: auto;") |
|||
.attr("preserveAspectRatio", "xMidYMin") |
|||
.append("svg:g") |
|||
.attr("transform", "translate(" + r + "," + r + ")"); |
|||
|
|||
|
|||
var pie = d3.layout.pie().value(function(d){return d.value;}); |
|||
|
|||
// declare an arc generator function |
|||
var arc = d3.svg.arc().outerRadius(r); |
|||
|
|||
// select paths, use arc generator to draw |
|||
var arcs = vis.selectAll("g.slice{{=pool}}") |
|||
.data(pie) |
|||
.enter() |
|||
.append("svg:g") |
|||
.attr("class", "slice{{=pool}}") |
|||
.attr("id", "slice") |
|||
.on("mouseover", function(d){ |
|||
d3.select("#tooltip{{=pool}}") |
|||
.style("left", d3.event.pageX + "px") |
|||
.style("top", d3.event.pageY + "px") |
|||
.style("opacity", 1) |
|||
.select("#value{{=pool}}") |
|||
.text(d.data.value); |
|||
d3.select("#tooltip{{=pool}}") |
|||
.select("#finderr{{=pool}}") |
|||
.text(d.data.label); |
|||
}); |
|||
|
|||
arcs.append("svg:path") |
|||
.attr("fill", function(d, i){ |
|||
return color(i); |
|||
}) |
|||
.attr("d", function (d) { |
|||
return arc(d); |
|||
}); |
|||
|
|||
var legend = vis.selectAll('.legend') |
|||
.data(color.domain()) |
|||
.enter() |
|||
.append('g') |
|||
.attr('class', 'legend') |
|||
.attr('id', {{=JSON.stringify(pool)}}) |
|||
.attr('transform', function(d, i) { |
|||
var height = legendRectSize + legendSpacing; |
|||
var offset = height * color.domain().length / 2; |
|||
var horz = 12 * legendRectSize; |
|||
var vert = i * height; |
|||
return 'translate(' + horz + ',' + vert + ')'; |
|||
}); |
|||
|
|||
legend.append('rect') |
|||
.attr('width', legendRectSize) |
|||
.attr('height', legendRectSize) |
|||
.style('fill', color) |
|||
.style('stroke', color); |
|||
|
|||
legend.append('text') |
|||
.attr('x', legendRectSize + legendSpacing) |
|||
.attr('y', legendRectSize - legendSpacing) |
|||
.text(function(d, i) { |
|||
return data[i].label; |
|||
}); |
|||
</script> |
|||
{{ } }} |
|||
{{ } }} |
|||
|
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js'; |
|||
|
|||
$(function() { |
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
var stats = JSON.parse(e.data); |
|||
for (var pool in stats.pools) { |
|||
$('#statsMiners' + pool).text(stats.pools[pool].minerCount); |
|||
$('#statsWorkers' + pool).text(stats.pools[pool].workerCount); |
|||
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays); |
|||
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks); |
|||
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)).toFixed(8)); |
|||
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks); |
|||
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff); |
|||
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols)); |
|||
$('#statsNetworkConnections' + pool).text(stats.pools[pool].poolStats.networkConnections); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function getReadableNetworkHashRateString(hashrate){ |
|||
hashrate = (hashrate * 1000000); |
|||
if (hashrate < 1000000) |
|||
return '0 Sol'; |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
return hashrate.toFixed(2) + byteUnits[i]; |
|||
} |
|||
</script> |
@ -0,0 +1,64 @@ |
|||
<style> |
|||
|
|||
#topCharts { |
|||
padding: 18px; |
|||
} |
|||
|
|||
#topCharts > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
|
|||
.chartWrapper { |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
|
|||
.chartLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
|
|||
.chartHolder { |
|||
|
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
</style> |
|||
|
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Pool</th> |
|||
<th>Algo</th> |
|||
<th>Workers</th> |
|||
<th>Valid Shares</th> |
|||
<th>Invalid Shares</th> |
|||
<th>Total Blocks</th> |
|||
<th>Pending</th> |
|||
<th>Confirmed</th> |
|||
<th>Orphaned</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<tr class="pure-table-odd"> |
|||
<td>{{=it.stats.pools[pool].name}}</td> |
|||
<td>{{=it.stats.pools[pool].algorithm}}</td> |
|||
<td>{{=Object.keys(it.stats.pools[pool].workers).length}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.invalidShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validBlocks}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.pending}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.confirmed}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.orphaned}}</td> |
|||
<td>{{=it.stats.pools[pool].hashrateString}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
@ -0,0 +1,94 @@ |
|||
<style> |
|||
#bottomNotes { |
|||
display: block; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-bottom: 18px; |
|||
} |
|||
#topPool { |
|||
padding-top: 18px; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
} |
|||
#topPool > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.poolWrapper { |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.poolLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
.poolMinerTable { |
|||
|
|||
} |
|||
table { |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
<script type="text/javascript"> |
|||
function searchKeyPress(e) |
|||
{ |
|||
// look for window.event in case event isn't passed in |
|||
e = e || window.event; |
|||
if (e.keyCode == 13) |
|||
{ |
|||
document.getElementById('btnSearch').click(); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
$(document).ready(function(){ |
|||
$('.btn-lg').click(function(){ |
|||
window.location = "workers/" + $('.input-lg').val(); |
|||
}); |
|||
}); |
|||
</script> |
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ var i=0; for(var pool in it.stats.pools) { }} |
|||
<div id="topPool"> |
|||
<div class="poolWrapper"> |
|||
<div class="poolLabel"> |
|||
<span style="float:right; margin-bottom: 8px;"> |
|||
<small>Miner Lookup: |
|||
<input type="text" class="form-control input-lg" onkeypress="return searchKeyPress(event);"> |
|||
<span class="input-group-btn"> |
|||
<button class="btn btn-default btn-lg" type="button">Lookup</button> |
|||
</span> |
|||
</small> |
|||
</span> |
|||
{{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners |
|||
<small><i class="fa fa-users"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners |
|||
<i class="fa fa-rocket"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers |
|||
<i class="fa fa-cog"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].shareCount}}</span> Shares </small> |
|||
</div> |
|||
<div class="poolMinerTable"> |
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Address</th> |
|||
<th>Shares</th> |
|||
<th>Efficiency</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var worker in it.stats.pools[pool].miners) { }} |
|||
{{var workerstat = it.stats.pools[pool].miners[worker];}} |
|||
<tr class="pure-table-odd"> |
|||
<td><a href="/workers/{{=worker.split('.')[0]}}">{{=worker.length > 40 ? worker.substring(0, 20) + '...' + worker.substring(worker.length-20, worker.length): worker}}</a></td> |
|||
<td>{{=Math.round(workerstat.currRoundShares * 100) / 100}}</td> |
|||
<td>{{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}}</td> |
|||
<td>{{=workerstat.hashrateString}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
@ -0,0 +1,100 @@ |
|||
var docCookies = { |
|||
getItem: function (sKey) { |
|||
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; |
|||
}, |
|||
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { |
|||
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } |
|||
var sExpires = ""; |
|||
if (vEnd) { |
|||
switch (vEnd.constructor) { |
|||
case Number: |
|||
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; |
|||
break; |
|||
case String: |
|||
sExpires = "; expires=" + vEnd; |
|||
break; |
|||
case Date: |
|||
sExpires = "; expires=" + vEnd.toUTCString(); |
|||
break; |
|||
} |
|||
} |
|||
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); |
|||
return true; |
|||
}, |
|||
removeItem: function (sKey, sPath, sDomain) { |
|||
if (!sKey || !this.hasItem(sKey)) { return false; } |
|||
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); |
|||
return true; |
|||
}, |
|||
hasItem: function (sKey) { |
|||
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); |
|||
} |
|||
}; |
|||
|
|||
var password = docCookies.getItem('password'); |
|||
|
|||
|
|||
function showLogin(){ |
|||
$('#adminCenter').hide(); |
|||
$('#passwordForm').show(); |
|||
} |
|||
|
|||
function showAdminCenter(){ |
|||
$('#passwordForm').hide(); |
|||
$('#adminCenter').show(); |
|||
} |
|||
|
|||
function tryLogin(){ |
|||
apiRequest('pools', {}, function(response){ |
|||
showAdminCenter(); |
|||
displayMenu(response.result) |
|||
}); |
|||
} |
|||
|
|||
function displayMenu(pools){ |
|||
$('#poolList').after(Object.keys(pools).map(function(poolName){ |
|||
return '<li class="poolMenuItem"><a href="#">' + poolName + '</a></li>'; |
|||
}).join('')); |
|||
} |
|||
|
|||
function apiRequest(func, data, callback){ |
|||
var httpRequest = new XMLHttpRequest(); |
|||
httpRequest.onreadystatechange = function(){ |
|||
if (httpRequest.readyState === 4 && httpRequest.responseText){ |
|||
if (httpRequest.status === 401){ |
|||
docCookies.removeItem('password'); |
|||
$('#password').val(''); |
|||
showLogin(); |
|||
alert('Incorrect Password'); |
|||
} |
|||
else{ |
|||
var response = JSON.parse(httpRequest.responseText); |
|||
callback(response); |
|||
} |
|||
} |
|||
}; |
|||
httpRequest.open('POST', '/api/admin/' + func); |
|||
data.password = password; |
|||
httpRequest.setRequestHeader('Content-Type', 'application/json'); |
|||
httpRequest.send(JSON.stringify(data)); |
|||
} |
|||
|
|||
if (password){ |
|||
tryLogin(); |
|||
} |
|||
else{ |
|||
showLogin(); |
|||
} |
|||
|
|||
$('#passwordForm').submit(function(event){ |
|||
event.preventDefault(); |
|||
password = $('#password').val(); |
|||
if (password){ |
|||
if ($('#remember').is(':checked')) |
|||
docCookies.setItem('password', password, Infinity); |
|||
else |
|||
docCookies.setItem('password', password); |
|||
tryLogin(); |
|||
} |
|||
return false; |
|||
}); |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 9.6 KiB |
After Width: | Height: | Size: 21 KiB |
After Width: | Height: | Size: 797 B |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,30 @@ |
|||
$(function(){ |
|||
|
|||
var hotSwap = function(page, pushSate){ |
|||
if (pushSate) history.pushState(null, null, '/' + page); |
|||
$('.pure-menu-selected').removeClass('pure-menu-selected'); |
|||
$('a[href="/' + page + '"]').parent().addClass('pure-menu-selected'); |
|||
$.get("/get_page", {id: page}, function(data){ |
|||
$('main').html(data); |
|||
}, 'html') |
|||
}; |
|||
|
|||
$('.hot-swapper').click(function(event){ |
|||
if (event.which !== 1) return; |
|||
var pageId = $(this).attr('href').slice(1); |
|||
hotSwap(pageId, true); |
|||
event.preventDefault(); |
|||
return false; |
|||
}); |
|||
|
|||
window.addEventListener('load', function() { |
|||
setTimeout(function() { |
|||
window.addEventListener("popstate", function(e) { |
|||
hotSwap(location.pathname.slice(1)); |
|||
}); |
|||
}, 0); |
|||
}); |
|||
|
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
}); |
@ -0,0 +1,246 @@ |
|||
var workerHashrateData; |
|||
var workerHashrateChart; |
|||
var workerHistoryMax = 160; |
|||
|
|||
var statData; |
|||
var totalHash; |
|||
var totalImmature; |
|||
var totalBal; |
|||
var totalPaid; |
|||
var totalShares; |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
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: [] |
|||
}); |
|||
for (var wh in statData.history[w]) { |
|||
a.hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); |
|||
} |
|||
if (a.hashrate.length > workerHistoryMax) { |
|||
workerHistoryMax = a.hashrate.length; |
|||
} |
|||
} |
|||
|
|||
var i=0; |
|||
workerHashrateData = []; |
|||
for (var worker in workers){ |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
disabled: (i > Math.min((_workerCount-1), 3)), |
|||
values: workers[worker].hashrate |
|||
}); |
|||
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 wh = statData.history[w][statData.history[w].length - 1];
|
|||
var foundWorker = false; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
if (workerHashrateData[i].key === worker) { |
|||
foundWorker = 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]); |
|||
break; |
|||
} |
|||
} |
|||
if (!foundWorker) { |
|||
var hashrate = []; |
|||
hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
values: hashrate |
|||
}); |
|||
rebuildWorkerDisplay(); |
|||
return true; |
|||
} |
|||
} |
|||
triggerChartUpdates(); |
|||
return false; |
|||
} |
|||
|
|||
function calculateAverageHashrate(worker) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) { |
|||
if (worker == null || workerHashrateData[i].key === worker) { |
|||
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; |
|||
totalPaid = statData.paid; |
|||
totalBal = statData.balance; |
|||
totalImmature = statData.immature; |
|||
totalShares = statData.totalShares; |
|||
// do some calculations
|
|||
var _blocktime = 250; |
|||
var _networkHashRate = parseFloat(statData.networkSols) * 1.2; |
|||
var _myHashRate = (totalHash / 1000000) * 2; |
|||
var luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3); |
|||
// update miner stats
|
|||
$("#statsHashrate").text(getReadableHashRateString(totalHash)); |
|||
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null))); |
|||
$("#statsLuckDays").text(luckDays); |
|||
$("#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].hashrate)); |
|||
$("#statsHashrateAvg"+htmlSafeWorkerName).text(getReadableHashRateString(calculateAverageHashrate(saneWorkerName))); |
|||
$("#statsLuckDays"+htmlSafeWorkerName).text(statData.workers[w].luckDays); |
|||
$("#statsPaid"+htmlSafeWorkerName).text(statData.workers[w].paid); |
|||
$("#statsBalance"+htmlSafeWorkerName).text(statData.workers[w].balance); |
|||
$("#statsShares"+htmlSafeWorkerName).text(Math.round(statData.workers[w].currRoundShares * 100) / 100); |
|||
$("#statsDiff"+htmlSafeWorkerName).text(statData.workers[w].diff); |
|||
} |
|||
} |
|||
function addWorkerToDisplay(name, htmlSafeName, workerObj) { |
|||
var htmlToAdd = ""; |
|||
htmlToAdd = '<div class="boxStats" id="boxStatsLeft" style="float:left; margin: 9px; min-width: 260px;"><div class="boxStatsList">'; |
|||
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><i class="fa fa-tachometer"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</div>'; |
|||
htmlToAdd+='<div><i class="fa fa-tachometer"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name))+'</span> (Avg)</div>'; |
|||
htmlToAdd+='<div><i class="fa fa-shield"></i> <small>Diff:</small> <span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>'; |
|||
htmlToAdd+='<div><i class="fa fa-cog"></i> <small>Shares:</small> <span id="statsShares'+htmlSafeName+'">'+(Math.round(workerObj.currRoundShares * 100) / 100)+'</span></div>'; |
|||
htmlToAdd+='<div><i class="fa fa-gavel"></i> <small>Luck <span id="statsLuckDays'+htmlSafeName+'">'+workerObj.luckDays+'</span> Days</small></div>'; |
|||
htmlToAdd+='<div><i class="fa fa-money"></i> <small>Bal: <span id="statsBalance'+htmlSafeName+'">'+workerObj.balance+'</span></small></div>'; |
|||
htmlToAdd+='<div><i class="fa fa-money"></i> <small>Paid: <span id="statsPaid'+htmlSafeName+'">'+workerObj.paid+'</span></small></div>'; |
|||
htmlToAdd+='</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(); |
|||
} |
|||
}); |
|||
}); |
@ -0,0 +1,143 @@ |
|||
var poolHashrateData; |
|||
var poolHashrateChart; |
|||
|
|||
var statData; |
|||
var poolKeys; |
|||
|
|||
function buildChartData(){ |
|||
var pools = {}; |
|||
|
|||
poolKeys = []; |
|||
for (var i = 0; i < statData.length; i++){ |
|||
for (var pool in statData[i].pools){ |
|||
if (poolKeys.indexOf(pool) === -1) |
|||
poolKeys.push(pool); |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < statData.length; i++) { |
|||
var time = statData[i].time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++){ |
|||
var pName = poolKeys[f]; |
|||
var a = pools[pName] = (pools[pName] || { |
|||
hashrate: [] |
|||
}); |
|||
if (pName in statData[i].pools){ |
|||
a.hashrate.push([time, statData[i].pools[pName].hashrate]); |
|||
} |
|||
else{ |
|||
a.hashrate.push([time, 0]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
poolHashrateData = []; |
|||
for (var pool in pools){ |
|||
poolHashrateData.push({ |
|||
key: pool, |
|||
values: pools[pool].hashrate |
|||
}); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
} |
|||
} |
|||
|
|||
function calculateAverageHashrate(pool) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < poolHashrateData[i].values.length; ii++) { |
|||
if (pool == null || poolHashrateData[i].key === pool) { |
|||
count++; |
|||
avg += parseFloat(poolHashrateData[i].values[ii][1]); |
|||
} |
|||
} |
|||
if (count > total) |
|||
total = count; |
|||
} |
|||
avg = avg / total; |
|||
return avg; |
|||
} |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
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 displayCharts(){ |
|||
nv.addGraph(function() { |
|||
poolHashrateChart = nv.models.lineChart() |
|||
.margin({left: 80, right: 30}) |
|||
.x(function(d){ return d[0] }) |
|||
.y(function(d){ return d[1] }) |
|||
.useInteractiveGuideline(true); |
|||
|
|||
poolHashrateChart.xAxis.tickFormat(timeOfDayFormat); |
|||
|
|||
poolHashrateChart.yAxis.tickFormat(function(d){ |
|||
return getReadableHashRateString(d); |
|||
}); |
|||
|
|||
d3.select('#poolHashrate').datum(poolHashrateData).call(poolHashrateChart); |
|||
|
|||
return poolHashrateChart; |
|||
}); |
|||
} |
|||
|
|||
function triggerChartUpdates(){ |
|||
poolHashrateChart.update(); |
|||
} |
|||
|
|||
nv.utils.windowResize(triggerChartUpdates); |
|||
|
|||
$.getJSON('/api/pool_stats', function(data){ |
|||
statData = data; |
|||
buildChartData(); |
|||
displayCharts(); |
|||
}); |
|||
|
|||
statsSource.addEventListener('message', function(e){ |
|||
var stats = JSON.parse(e.data); |
|||
statData.push(stats); |
|||
|
|||
var newPoolAdded = (function(){ |
|||
for (var p in stats.pools){ |
|||
if (poolKeys.indexOf(p) === -1) |
|||
return true; |
|||
} |
|||
return false; |
|||
})(); |
|||
|
|||
if (newPoolAdded || Object.keys(stats.pools).length > poolKeys.length){ |
|||
buildChartData(); |
|||
displayCharts(); |
|||
} |
|||
else { |
|||
var time = stats.time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
if (poolHashrateData[i].key === pool) { |
|||
poolHashrateData[i].values.shift(); |
|||
poolHashrateData[i].values.push([time, pool in stats.pools ? stats.pools[pool].hashrate : 0]); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
triggerChartUpdates(); |
|||
} |
|||
}); |
@ -0,0 +1,68 @@ |
|||
html, button, input, select, textarea, .pure-g [class *= "pure-u"], .pure-g-r [class *= "pure-u"]{ |
|||
font-family: 'Open Sans', sans-serif; |
|||
} |
|||
|
|||
html{ |
|||
background: #2d2d2d; |
|||
overflow-y: scroll; |
|||
} |
|||
|
|||
body{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
max-width: 1160px; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
header > .home-menu{ |
|||
background: inherit !important; |
|||
height: 54px; |
|||
display: flex; |
|||
} |
|||
|
|||
header > .home-menu > a.pure-menu-heading, header > .home-menu > ul, header > .home-menu > ul > li{ |
|||
display: flex !important; |
|||
align-items: center; |
|||
justify-content: center; |
|||
line-height: normal !important; |
|||
} |
|||
|
|||
header > .home-menu > a.pure-menu-heading{ |
|||
color: white; |
|||
font-size: 1.5em; |
|||
} |
|||
|
|||
header > .home-menu > ul > li > a{ |
|||
color: #ced4d9; |
|||
} |
|||
|
|||
header > .home-menu > ul > li > a:hover, header > .home-menu > ul > li > a:focus{ |
|||
background: inherit !important; |
|||
} |
|||
|
|||
header > .home-menu > ul > li > a:hover, header > .home-menu > ul > li.pure-menu-selected > a{ |
|||
color: white; |
|||
} |
|||
|
|||
main{ |
|||
background-color: #ebf4fa; |
|||
position: relative; |
|||
} |
|||
|
|||
footer{ |
|||
text-align: center; |
|||
color: #b3b3b3; |
|||
text-decoration: none; |
|||
font-size: 0.8em; |
|||
padding: 15px; |
|||
line-height: 24px; |
|||
} |
|||
|
|||
footer a{ |
|||
color: #fff; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
footer iframe{ |
|||
vertical-align: middle; |
|||
} |
@ -0,0 +1,102 @@ |
|||
<!doctype html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
|
|||
<title>piratepool.io - mining pool for PirateChain (ARRR) - the most private and anonymous mineable cryptocurrency - Pirate</title> |
|||
|
|||
<meta name="description" content="The first and largest mining pool for PirateChain (ARRR). Low fees with 66% of proceeds donated to development and marketing efforts. The most private and anonymous cryptocurrency in existence is mineable here on the Equihash algorithm by CPU, GPU, ASIC, NiceHash. - Pirate"> |
|||
<meta name="keywords" content="PirateChain,Pirate,ARRR,mining,pool,equihash,komodo,dpow,privacy,private,freedom"> |
|||
<meta name="robots" content="index, follow"> |
|||
|
|||
<meta property="og:locale" content="en_EN"> |
|||
<meta property="og:type" content="website"> |
|||
<meta property="og:title" content="PiratePool.io - mining pool for PirateChain (ARRR) - the most private mineable cryptocurrency - Pirate"> |
|||
<meta property="og:site_name" content="PiratePool.io - mining pool for PirateChain (ARRR)"> |
|||
<meta property="og:description" content="The first and largest mining pool for PirateChain (ARRR). Low fees with 66% of proceeds donated to development and marketing efforts. The most private and anonymous cryptocurrency in existence is mineable here on the Equihash algorithm by CPU, GPU, ASIC, NiceHash. - Pirate"> |
|||
|
|||
<meta property="og:url" content="https://piratepool.io"> |
|||
<meta property="og:image" content="https://piratepool.io/static/pirate128.png"> |
|||
<meta property="og:image:secure_url" content="https://piratepool.io/static/pirate128.png"> |
|||
<meta property="og:image:width" content="128"> |
|||
<meta property="og:image:height" content="128"> |
|||
<meta name="twitter:card" content="summary_large_image"> |
|||
<meta name="twitter:description" content="The first and largest mining pool for PirateChain (ARRR). Low fees with 66% of proceeds donated to development and marketing efforts. The most private and anonymous cryptocurrency in existence is mineable here on the Equihash algorithm by CPU, GPU, ASIC, NiceHash. - Pirate"> |
|||
<meta name="twitter:title" content="PiratePool.io - mining pool for PirateChain (ARRR) - the most private mineable cryptocurrency - Pirate"> |
|||
<meta name="twitter:site" content="@webworker01"> |
|||
<meta name="twitter:image" content="https://piratepool.io/static/pirate128.png"> |
|||
<meta name="twitter:creator" content="@webworker01"> |
|||
|
|||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Bai+Jamjuree|Roboto" /> |
|||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous" /> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/1.0.0/base-min.css" integrity="sha256-XsW+lsCsaHybdLLnHWsTuwb9A2NuD2hVVlsxtiipTZQ=" crossorigin="anonymous" /> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/1.0.0/pure-min.css" integrity="sha256-Q0zCrUs2IfXWYx0uMKJfG93CvF6oVII21waYsAV4/8Q=" crossorigin="anonymous" /> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.css" integrity="sha256-bmrwGjHOoD7azP+ZpGcOOitUNUGNRjwzjK1bZeTK6fI=" crossorigin="anonymous" /> |
|||
<link rel="stylesheet" href="/static/style.css"> |
|||
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script> |
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js" integrity="sha256-dsOXGNHAo/syFnazt+KTBsCQeRmlcW1XKL0bCK4Baec=" crossorigin="anonymous"></script> |
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.6/nv.d3.min.js" integrity="sha256-Eg29ohiE9Hzc/t5whG/QK/B8MGmrO4wkF6WGuSsx0VU=" crossorigin="anonymous"></script> |
|||
<script src="/static/main.js"></script> |
|||
|
|||
<link rel="shortcut icon" type="image/png" href="/static/pirate128.png"/> |
|||
<link rel="apple-touch-icon" sizes="57x57" href="/static/favicon/apple-icon-57x57.png"> |
|||
<link rel="apple-touch-icon" sizes="60x60" href="/static/favicon/apple-icon-60x60.png"> |
|||
<link rel="apple-touch-icon" sizes="72x72" href="/static/favicon/apple-icon-72x72.png"> |
|||
<link rel="apple-touch-icon" sizes="76x76" href="/static/favicon/apple-icon-76x76.png"> |
|||
<link rel="apple-touch-icon" sizes="114x114" href="/static/favicon/apple-icon-114x114.png"> |
|||
<link rel="apple-touch-icon" sizes="120x120" href="/static/favicon/apple-icon-120x120.png"> |
|||
<link rel="apple-touch-icon" sizes="144x144" href="/static/favicon/apple-icon-144x144.png"> |
|||
<link rel="apple-touch-icon" sizes="152x152" href="/static/favicon/apple-icon-152x152.png"> |
|||
<link rel="apple-touch-icon" sizes="180x180" href="/static/favicon/apple-icon-180x180.png"> |
|||
<link rel="icon" type="image/png" sizes="192x192" href="/static/favicon/android-icon-192x192.png"> |
|||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon/favicon-32x32.png"> |
|||
<link rel="icon" type="image/png" sizes="96x96" href="/static/favicon/favicon-96x96.png"> |
|||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon/favicon-16x16.png"> |
|||
<link rel="manifest" href="/static/favicon/manifest.json"> |
|||
<meta name="msapplication-TileColor" content="#BB9645"> |
|||
<meta name="msapplication-TileImage" content="/static/favicon/ms-icon-144x144.png"> |
|||
<meta name="theme-color" content="#BB9645"> |
|||
|
|||
<meta name="google-site-verification" content="JxkbxOT-aLGRVzTv8ES0RQ7EaWdSGZvOfZOUVmF1Xm8" /> |
|||
</head> |
|||
<body> |
|||
<header> |
|||
<div class="home-menu pure-menu pure-menu-horizontal"> |
|||
<ul class="pure-menu-list"> |
|||
<li class="pure-menu-item {{? it.selected.length < 1 }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/"><i class="fa fa-home"></i>Home</a> |
|||
</li> |
|||
<li class="pure-menu-item {{? it.selected === 'getting_started' }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/getting_started"><i class="fas fa-rocket fa-fw"></i>Getting Started</a> |
|||
</li> |
|||
<li class="pure-menu-item {{? it.selected === 'stats' }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/stats"><i class="fas fa-chart-bar fa-fw"></i>Pool Stats</a> |
|||
</li> |
|||
<!-- <li class="pure-menu-item {{? it.selected === 'tbs' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/tbs"><i class="fas fa-table fa-fw"></i>Tab Stats</a> |
|||
</li> --> |
|||
<li class="pure-menu-item {{? it.selected === 'workers' }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/workers"><i class="fas fa-cogs fa-fw"></i>Workers</a> |
|||
</li> |
|||
<li class="pure-menu-item {{? it.selected === 'payments' }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/payments"><i class="fab fa-btc fa-fw"></i>Payments</a> |
|||
</li> |
|||
<li class="pure-menu-item {{? it.selected === 'api' }}pure-menu-selected{{?}}"> |
|||
<a class="pure-menu-link hot-swapper" href="/api"><i class="fas fa-code fa-fw"></i>API</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</header> |
|||
<main> |
|||
{{=it.page}} |
|||
</main> |
|||
<footer> |
|||
No warranties matey! If ye be 'aving any troubles, invoke yarrr right t' parlay at <!-- <a href="https://discord.gg/rZCXtCC" target="_blank" rel="noopener noreferrer" class="highlight">#pirate</a> --> <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> |
|||
<br> |
|||
<3 <a href="https://webworker.sh" alt="webworker01" target="_blank" rel="noopener noreferrer">🕷️</a> · Built with <a href="https://github.com/webworker01/knomp" target="_blank" rel="noopener noreferrer" class="highlight">knomp</a> |
|||
</footer> |
|||
</body> |
|||
</html> |
@ -0,0 +1,50 @@ |
|||
<div> |
|||
|
|||
<style> |
|||
#passwordForm, #adminCenter{ |
|||
display: none; |
|||
} |
|||
#adminCenter{ |
|||
display: flex; |
|||
flex-flow: row; |
|||
} |
|||
#leftMenu{ |
|||
flex: 0 0 200px; |
|||
} |
|||
#editForm{ |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
|
|||
<form class="pure-form pure-form-stacked" id="passwordForm"> |
|||
<fieldset> |
|||
<legend>Password</legend> |
|||
|
|||
<input id="password" type="password" placeholder="Password"> |
|||
|
|||
<label for="remember" class="pure-checkbox"> |
|||
<input id="remember" type="checkbox"> Stay Logged In |
|||
</label> |
|||
|
|||
<button type="submit" class="pure-button pure-button-primary">Log In</button> |
|||
</fieldset> |
|||
</form> |
|||
|
|||
<div id="adminCenter"> |
|||
|
|||
<div class="pure-menu pure-menu-open" id="leftMenu"> |
|||
<a class="pure-menu-heading">Administration</a> |
|||
<ul> |
|||
<li id="addPool"><a href="#">Add Pool</a></li> |
|||
<li class="pure-menu-heading" id="poolList">Current Pools</li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div id="editForm"></div> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<script src="/static/admin.js"></script> |
|||
|
|||
</div> |
@ -0,0 +1,11 @@ |
|||
<div style="padding: 18px;"> |
|||
API - The API is work in progress and is subject to change during development. |
|||
<ul> |
|||
<li><a href="/api/stats">/api/stats</a> global pool stats</li> |
|||
<li><a href="/api/blocks">/api/blocks</a> global block stats</li> |
|||
<li><a href="/api/pool_stats">/api/pool_stats</a> - historical stats</li> |
|||
<li><a href="/api/payments">/api/payments</a> - payment history</li> |
|||
<li><a href="/api/worker_stats?addr">/api/worker_stats?<replace address here></a> - historical time per pool json </li> |
|||
<li><a href="/api/live_stats">/api/live_stats</a> - live stats (eventsource)</li> |
|||
</ul> |
|||
</div> |
@ -0,0 +1,219 @@ |
|||
<div class="pure-g gettingStarted"> |
|||
<div class="pure-u-1-4" style="display:flex; flex:2;"> |
|||
<div class="pure-menu pure-menu-open gettingStartedMenu"> |
|||
<h1 class="pure-menu-heading">Getting Started</h1> |
|||
|
|||
<ul class="pure-menu-list"> |
|||
<li class="pure-menu-item"><a href="#poolDetails" class="pure-menu-link">Pool Details</a></li> |
|||
<li class="pure-menu-item"><a href="#payments" class="pure-menu-link">Payout Information</a></li> |
|||
<li class="pure-menu-item"><a href="#createWallet" class="pure-menu-link">Create Wallet</a></li> |
|||
<li class="pure-menu-item"><a href="#gpuMining" class="pure-menu-link">GPU Mining Software</a></li> |
|||
<li class="pure-menu-item"><a href="#cpuMining" class="pure-menu-link">CPU Mining Software</a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
<div class="pure-u-3-4 pure-responsive-disable"> |
|||
{{? (function(){ |
|||
if (!it.portalConfig.switching) return false; |
|||
for (var p in it.portalConfig.switching){ |
|||
if (it.portalConfig.switching[p].enabled) |
|||
return true; |
|||
} |
|||
return false; |
|||
})() |
|||
}} |
|||
<div class="menuHeader">Coin-Switching Ports</div> |
|||
{{?}} |
|||
|
|||
<div class="menuList"> |
|||
{{ for (var p in it.portalConfig.switching){ |
|||
if (!it.portalConfig.switching[p].enabled) continue; |
|||
var info = { |
|||
algo: p, |
|||
ports: {}, |
|||
host: it.portalConfig.website.stratumHost |
|||
}; |
|||
info.ports[it.portalConfig.switching[p].port] = {diff: it.portalConfig.switching[p].diff}; |
|||
info = JSON.stringify(info).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=p}}</a> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
<h1 class="menuHeader" id="poolDetails">Miner Configuration</h1> |
|||
<div class="menuList" id="coinList"> |
|||
{{ if (it.portalConfig.gettingStartedPopups) { }} |
|||
{{ for (var pool in it.poolsConfigs) { |
|||
var info = JSON.stringify({ |
|||
coin: it.poolsConfigs[pool].coin, |
|||
algo: it.poolsConfigs[pool].coin.algorithm, |
|||
ports: it.poolsConfigs[pool].ports, |
|||
host: it.portalConfig.website.stratumHost |
|||
}).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=pool}}</a> |
|||
{{ } }} |
|||
{{ } else { }} |
|||
{{ for (var pool in it.poolsConfigs) { }} |
|||
{{if (String(pool) == 'pirate') { continue; } }} |
|||
<code> |
|||
<h2 class="coinInfoHeader">{{=it.poolsConfigs[pool].coin.name}} Configuration:</h2> |
|||
<p class="highlight">¡¡¡Do not mine to an exchange address!!! <a href="#createWallet">Create a wallet to mine to!</a></p> |
|||
<table style="margin-top:10px;"> |
|||
<tr> |
|||
<td>Username: </td> |
|||
<td>{{ if (it.poolsConfigs[pool].coin && it.poolsConfigs[pool].coin.privateChain) { }} |
|||
your {{=it.poolsConfigs[pool].coin.name}} <span class="highlight">zs1</span> sapling address |
|||
{{ } else if (it.poolsConfigs[pool].coin) { }} |
|||
your {{=it.poolsConfigs[pool].coin.name}} wallet address |
|||
{{ } else { }} |
|||
your public key |
|||
{{ } }} |
|||
</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Password: </td> |
|||
<td>anything</td> |
|||
</tr> |
|||
<tr> |
|||
<td>Algorithm: </td> |
|||
<td>{{=it.poolsConfigs[pool].coin.algorithm}}</td> |
|||
</tr> |
|||
{{ for (var port in it.poolsConfigs[pool].ports) { }} |
|||
<tr> |
|||
<td class="coinInfoData">{{= it.poolsConfigs[pool].ports[port].label ? it.poolsConfigs[pool].ports[port].label : 'URL'}} <span class="muted">(diff {{=it.poolsConfigs[pool].ports[port].diff}})</span>: </td> |
|||
<td>stratum+tcp://{{=it.portalConfig.website.stratumHost}}:{{=port}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
|||
</code> |
|||
{{ } }} |
|||
|
|||
<div class="gettingStartedContent"> |
|||
If you have multiple mining rigs you can add a label at the end of your username to see stats broken down by rig on the worker stats page. |
|||
</div> |
|||
<code> |
|||
<h3 class="coinInfoHeader">DSTM</h3> |
|||
zm --server {{=it.portalConfig.website.stratumHost}} --port {{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} --user zs1nrn7h684wq4429pz8a20r49dmmhd3ey07u38f8m2n9du33ashd0xcnls3lfz0spdvn26z3r4jrv<span class="highlight">.myrigname</span> --pass x -dev 0 1 --temp-target=0:70,1:70 --intensity=0:0.5,1:0.5 --color --time |
|||
</code> |
|||
<code> |
|||
<h3 class="coinInfoHeader">EWBF</h3> |
|||
miner --server {{=it.portalConfig.website.stratumHost}} --user zs1nrn7h684wq4429pz8a20r49dmmhd3ey07u38f8m2n9du33ashd0xcnls3lfz0spdvn26z3r4jrv<span class="highlight">.myrigname</span> --pass x --port {{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} --fee 0 --pec --templimit 70 --intensity 50 |
|||
</code> |
|||
<code> |
|||
<h3 class="coinInfoHeader">Bminer</h3> |
|||
bminer -uri stratum://zs1nrn7h684wq4429pz8a20r49dmmhd3ey07u38f8m2n9du33ashd0xcnls3lfz0spdvn26z3r4jrv<span class="highlight">.myrigname</span>@{{=it.portalConfig.website.stratumHost}}:{{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} -max-temperature 70 -nofee |
|||
</code> |
|||
<code> |
|||
<h3 class="coinInfoHeader">Claymore's</h3> |
|||
ZecMiner64.exe -zpool {{=it.portalConfig.website.stratumHost}}:{{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} -zwal zs1nrn7h684wq4429pz8a20r49dmmhd3ey07u38f8m2n9du33ashd0xcnls3lfz0spdvn26z3r4jrv<span class="highlight">.myrigname</span> -zpsw x -allpools 1 |
|||
</code> |
|||
<code> |
|||
<h3 class="coinInfoHeader">Optiminer</h3> |
|||
optiminer-zcash -s {{=it.portalConfig.website.stratumHost}}:{{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} -u zs1nrn7h684wq4429pz8a20r49dmmhd3ey07u38f8m2n9du33ashd0xcnls3lfz0spdvn26z3r4jrv<span class="highlight">.myrigname</span> -p x |
|||
</code> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
<h1 class="menuHeader" id="payments">Payments</h1> |
|||
<div class="gettingStartedContent"> |
|||
<p>Payouts are currently scheduled once every 4 hours with a minimum payout of 21 ARRR.</p> |
|||
<p>There is a limit of ~200 recipients that can be included in a single payout, so the pool payouts are monitored and the min payout adjusted accordingly to ensure a constant flow of payments.</p> |
|||
<p>If payments stop going out, do not worry! Funds are safu! Blocks can still be found and will be credited to your worker! Please check <!-- the <a href="https://discord.gg/rZCXtCC">#pools-and-operators channel in discord</a>--><a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool in discord</a> to see if it's already being worked on.</p> |
|||
<p>Pending blocks have not yet been scanned by the payment processor. After being scanned, blocks will need to receive both <a href="https://komodoplatform.com/security-delayed-proof-of-work-dpow/" target="_blank" rel="noopener noreferrer">dPoW notarization</a> and 10 confirmations before being actually paid out.</p> |
|||
<p>On your worker stats page, <em class="highlight">pending</em> balance is the remaining estimated amount to be paid for blocks that |
|||
were scanned but not yet eligible for payout either because of |
|||
<a href="https://komodoplatform.com/new-feature-to-verify-dpow-notarizations/" target="_blank" rel="noopener noreferrer">dPoW</a> or min confirmations. |
|||
<em class="highlight">Balance</em> includes blocks that were scanned and already paid out but you have not yet met the min payout.<em class="highlight">*</em></p> |
|||
<p><em class="highlight">*Please note these 2 stats will only update when the payment processor runs which is currently every 4 hours.</em></p> |
|||
<p>Payments that show up on the payments page initially are in the process of constructing the z transaction. Once the transaction actually broadcasts to the network, the blocks paid out will become hyperlinked to the explorer and then should arrive in your wallet in the next block or two.</p> |
|||
</div> |
|||
|
|||
<h1 class="menuHeader" id="createWallet">Generate Wallet and Address</h1> |
|||
<div class="gettingStartedContent"> |
|||
<p><a href="https://medium.com/piratechain/pirateocean-wallet-guide-1cb80f70364c" target="_blank" rel="noopener noreferrer">The PirateOceanQT for PIRATE</a> is available. For wallet support please visit <a href="https://discord.gg/qReShun">#newpirates on Discord</a></p> |
|||
|
|||
<h4>CLI Wallet:</h4> |
|||
<ol> |
|||
<li><a href="https://docs.komodoplatform.com/komodo/install-Komodo-manually.html" target="_blank">Build Komodo</a></li> |
|||
<li>Launch PIRATE chain: |
|||
<code> |
|||
./komodod -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=136.243.102.225 |
|||
</code> |
|||
</li> |
|||
<li>Generate Z address: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_getnewaddress |
|||
</code> |
|||
</li> |
|||
<li>Securely backup your private key: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_exportkey "zaddr" |
|||
</code> |
|||
</li> |
|||
<li>Check your balance: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_gettotalbalance |
|||
</code> |
|||
</li> |
|||
<li> |
|||
For more information visit the <a href="https://pirate.black/" target="_blank">project website</a> |
|||
</li> |
|||
</ol> |
|||
</div> |
|||
|
|||
<h1 class="menuHeader" id="gpuMining">GPU Mining</h1> |
|||
<div class="gettingStartedContent"> |
|||
<p>There are a few decent GPU miners available. You will need to experiment to find which one works best for you. DYOR on this and be sure to virus scan all the things!</p> |
|||
<p>Here are a couple well-known GPU miners:</p> |
|||
<ul> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=2021765.0" target="_blank" rel="noopener noreferrer">dstm's ZCash / Equihash Nvidia Miner</a></li> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=1707546.0" target="_blank" rel="noopener noreferrer">EWBF's CUDA Zcash miner 0.3.4b</a></li> |
|||
<li><a href="https://www.bminer.me/releases/" target="_blank" rel="noopener noreferrer">Bminer Lite</a></li> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=1670733.0" target="_blank" rel="noopener noreferrer">Claymore's ZCash/BTG AMD GPU Miner</a></li> |
|||
<li><a href="https://github.com/Optiminer/OptiminerZcash" target="_blank" rel="noopener noreferrer">Optiminer AMD</a></li> |
|||
|
|||
</ul> |
|||
</div> |
|||
|
|||
<h1 class="menuHeader" id="cpuMining">CPU Mining</h1> |
|||
<div class="gettingStartedContent"> |
|||
<p>While it is possible to CPU mine with <b>komodod</b> directly like so:</p> |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE setgenerate true |
|||
</code> |
|||
<p>The chances to find a block solo with CPU mining only are already fairly low and most likely the reason you are here at all 🐸</p> |
|||
<p>The well-known CPU miner that can be used with the pool is <b>nheqminer</b>. This does <i>not</i> require a Nicehash account and can be used to connect to any equihash stratum.</p> |
|||
|
|||
<h4>Linux</h4> |
|||
<p>It is suggested that you attempt to build the CPU miner instead of using the pre-compiled binary so that optimizations for your CPU can be made during the build process</p> |
|||
<code> |
|||
sudo apt-get install cmake build-essential libboost-all-dev<br /> |
|||
cd ~<br /> |
|||
git clone -b Linux https://github.com/nicehash/nheqminer.git<br /> |
|||
cd ~/nheqminer/cpu_xenoncat/Linux/asm/<br /> |
|||
sh assemble.sh<br /> |
|||
cd ~/nheqminer/Linux_cmake/nheqminer_cpu<br /> |
|||
cmake . && make<br /> |
|||
sudo ln -s ~/nheqminer/Linux_cmake/nheqminer_cpu/nheqminer_cpu /usr/local/bin/nheqminer_cpu |
|||
</code> |
|||
<p>Now you can launch your miner! (replace the values as indicated changing the number of threads (-t) and your address for payouts (-u)</p> |
|||
<code> |
|||
nheqminer_cpu -t <i>numberofthreads</i> -l <i>stratumhost</i>:<i>stratumport</i> -u <i>youraddress</i> |
|||
</code> |
|||
|
|||
<h4>Windows</h4> |
|||
<p>Download and install the latest release for windows from <a href="https://github.com/nicehash/nheqminer/releases" target="_blank" rel="noopener noreferrer">the github repo</a></p> |
|||
<p>Replace the placeholders below as needed and start mining!</p> |
|||
<code> |
|||
nheqminer -t <i>numberofthreads</i> -l <i>stratumhost</i>:<i>stratumport</i> -u <i>youraddress</i> |
|||
</code> |
|||
|
|||
<h4>Additional Resources</h4> |
|||
<ul> |
|||
<li><a href="https://github.com/nicehash/nheqminer" target="_blank" rel="noopener noreferrer">nheqminer repo</a></li> |
|||
<li><a href="https://steemit.com/mining/@bobinson/zcash-cpu-mining-setup-on-linux" target="_blank" rel="noopener noreferrer">CPU Mining</a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
@ -0,0 +1,98 @@ |
|||
{{ function bigNumber(x){ return (x > 1000000000000) ? (x / 1000000000000).toFixed(1) + 'T' : (x > 1000000000) ? (x / 1000000000).toFixed(1) + 'B' : (x > 1000000) ? (x / 1000000).toFixed(1) + 'M' : (x > 1000) ? (x / 1000).toFixed(1) + 'K' : x.toFixed(1); } }} |
|||
<div> |
|||
<div class="alertbar"> |
|||
<!-- Stop mining to exchange addresses… --> |
|||
<!-- 300K+ ARRR donated to Onboarding/Dev and Marketing funds, made possible via KMD notary node income.<br> |
|||
Thank you for all who voted me into KMD notary node for 2019 in SH! ❤ 🐸--> |
|||
<!-- New piratepool.io discord support and discussion channel created! <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">join #piratepool</a> 🐸 --> |
|||
<!-- Payout strategy changed to PPLNT with 1 hour payout frequency! Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 --> |
|||
Miner Privacy Enabled! Search for your miner with Miner Lookup. Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 |
|||
</div> |
|||
<div class="pure-g boxWelcome"> |
|||
<div class="pure-u-1-3 pure-responsive-disable" style="display:flex; justify-content:center; align-items:center;"> |
|||
<img src="/static/pirate128.png" style="width:128px; -webkit-filter: drop-shadow(-1px -1px 0 #000); filter: drop-shadow(-1px -1px 0 #000);" alt="piratepool.io - knomp" /> |
|||
</div> |
|||
<div class="pure-u-2-3 pure-responsive-disable"> |
|||
<h1 style="font-size: 2em; text-align:left; font-weight:900; color:#BB9645; margin:5px 0 10px;" class="textshadow">piratepool.io</h1> |
|||
<h2 style="color:#FFF; font-weight:900; font-size:1.5em; margin:0 0 10px;">Mining Pool For PirateChain (ARRR)</h2> |
|||
<p> |
|||
Donating 66% of the pool fee to the PirateChain project!<br> |
|||
<em>33% to PIRATE dev and <a href="https://dexstats.info/onboarding.php" target="_blank" rel="noopener noreferrer">onboarding</a> <a href="https://explorer.pirate.black/address/RAzq6y7dsUKgfuzNjpzyGiuFzvrwuDheQw" target="_blank" rel="noopener noreferrer">fund</a> |
|||
<br /> |
|||
33% to <a href="https://explorer.pirate.black/address/RD5PhyAUhapsvj5ps2cCHozsXZfQSvDdrZ" target="_blank" rel="noopener noreferrer">PIRATE Marketing</a>!</em> |
|||
</p> |
|||
<table style="width: 100%; margin: 15px auto; text-align: left;"> |
|||
<tr><td style="width:50%;">Algorithm: </td><td>Equihash</td></tr> |
|||
<tr><td>Payout Strategy: </td><td>PPLNT</td></tr> |
|||
<tr><td>Payout Frequency: </td><td>{{=it.poolsConfigs['arrr'].paymentProcessing.paymentInterval/60/60}} Hours</td></tr> |
|||
<tr><td>Min Payout: </td><td>{{=it.poolsConfigs['arrr'].paymentProcessing.minimumPayment}} ARRR</td></tr> |
|||
<tr><td>Pool Fee: </td><td>1%</td></tr> |
|||
</table> |
|||
<p>Pirate chain, the most private and anonymous cryptocurrency, is mineable here. Learn more:</p> |
|||
<div class="pure-g"> |
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<a href="https://pirate.black/" target="_blank" rel="noopener noreferrer"><i class="fa fa-globe fa-fw"></i> Website</a><br /> |
|||
<a href="https://github.com/PirateNetwork" target="_blank" rel="noopener noreferrer"><i class="fab fa-github fa-fw"></i> Github</a><br /> |
|||
<a href="https://bitcointalk.org/index.php?topic=4979549" target="_blank" rel="noopener noreferrer"><i class="fab fa-bitcoin fa-fw"></i> Bitcoin Talk</a><br /> |
|||
<a href="https://miningpoolstats.stream/piratechain" target="_blank" rel="noopener noreferrer"><i class="fas fa-swimmer"></i> Alternative Pools</a> |
|||
</div> |
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<a href="https://explorer.pirate.black" target="_blank" rel="noopener noreferrer"><i class="fas fa-map fa-fw"></i> Explorer</a><br /> |
|||
<a href="https://discord.gg/qReShun" target="_blank" rel="noopener noreferrer"><i class="fab fa-discord fa-fw"></i> Discord</a><br /> |
|||
<a href="https://twitter.com/PirateChain" target="_blank" rel="noopener noreferrer"><i class="fab fa-twitter fa-fw"></i> Twitter</a><br /> |
|||
<a href="https://digitalprice.io/?inviter=4fdaf7" target="_blank" rel="noopener noreferrer"><i class="fas fa-chart-line fa-fw"></i> Trading at DigitalPrice.io</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div style="max-width:640px; margin:10px auto; padding: 10px 10px 0;"> |
|||
<p> |
|||
<a href="https://medium.com/piratechain/pirateocean-wallet-guide-1cb80f70364c" target="_blank" rel="noopener noreferrer">Download the latest PirateOcean wallet</a> and create a sapling address (<span class="highlight">starts with zs1</span>) and connect to the new stratum ports labeled as <strong><em>arrr</em></strong> on <a href="/getting_started" class="hot-swapper">Getting Started</a>. |
|||
</p> |
|||
<p> |
|||
{{ /* <span style="font-weight:900;">Sprout stratum has been retired! <a href="https://piratepool.io/static/sproutfinal.txt" target="_blank" style=" text-decoration:underline; text-decoration-color:#BB9645">Final Sprout Payouts Report</a></span> */ }} |
|||
</p> |
|||
<!-- <p> |
|||
You need to migrate sprout funds in your own zc wallet address by <span class="highlight">2019-02-01</span>. A quick guide on how to migrate can be <a href="https://github.com/PirateNetwork/docs/wiki/How-to-Migrate-Pirate-(ARRR)-from-sprout-address-to-sapling-address" target="_blank" rel="noopener noreferrer">found here</a>. |
|||
You could also try <a href="https://dexstats.info/pirateswap.php" target="_blank" rel="noopener noreferrer">this service to swap your coins</a>. |
|||
</p> --> |
|||
</div> |
|||
|
|||
<div class="pure-g boxStats"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<div class="l-box"> |
|||
<h3>{{=it.stats.pools[pool].name}} Pool Stats</h3> |
|||
<div class="boxStatsList"> |
|||
<ul> |
|||
<li><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</li> |
|||
<li><i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</li> |
|||
<li><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<div class="l-box"> |
|||
<h3>{{=it.stats.pools[pool].name}} Network Stats</h3> |
|||
<div class="boxStatsList"> |
|||
<ul> |
|||
<li><i class="fas fa-link fa-fw" aria-hidden="true"></i> Block Height: <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> Network Hash/s: <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></li> |
|||
<li><i class="fas fa-unlock-alt fa-fw" aria-hidden="true"></i> Difficulty: <span id="statsNetworkDiff{{=pool}}">{{=bigNumber(it.stats.pools[pool].poolStats.networkDiff)}}</span></li> |
|||
<!-- <li><i class="fas fa-coins fa-fw"></i> Total Supply: <span id="statsNetworkCoins{{=pool}}">{{=(it.stats.pools[pool].poolStats.networkBlocks*256).toLocaleString('en')}}</span></li> --> |
|||
<li><i class="fas fa-coins fa-fw"></i> Total Supply: <span id="statsNetworkCoins{{=pool}}">200,000,000</span></li> |
|||
<li><i class="fas fa-users fa-fw"></i> Node Connections: <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/home.js'; |
|||
</script> |
|||
</div> |
@ -0,0 +1,34 @@ |
|||
{{ function middleEllipsis(x) { return x.length > 40 ? x.substring(0, 20) + '...' + x.substring(x.length-20, x.length): x; } }} |
|||
{{ function checkCompromised(m) { var compromised = ['zs10jprfefpt2ra88u2ml96nzh3dmlqqqfeha5l7g0x0s0d2awme0eqfyt4w7r7pee0na8tcz6ezzj', 'zs12urwtysy5rkqzy3xgaxeu9g3ezf4fr0wfq44xrnf48uq6ug8qfxp059lms06khekppquvzadxyf', 'zs16sk9n6lyc8ulzkrf5xc72cr07hkyjvvj2emeqjel7xmn90qaf9mj2m8vt34yw45l7syqxlmfe5v', 'zs16xlsu48grwgsr29c9ztcn8jl0s47rtyucv3dw0n2e6y00w0wtt3d4ukqk4sq4sycrdhtxxg7g3k', 'zs19whs7s29a2kn5d4s8mpdad0m4setz86sq64kpv0t2v263mrqvuducp6nlmlwvynltam9zm9mr0p', 'zs1au66ft2gyu8ylgkw70vy4we2ktzum52esl94875w44mu8cmw2dcwfc29kahgmvfwcagwy39ctee', 'zs1c83zjak7ealmn92hejmqmjc7h3ysvglc9n95z79dhajth4fmhmrnv5zxdkufzg6l489y6hwf694', 'zs1d9myt9rpvu6grsl3ntg9wv0dfuj4vhp4rr0m3f940tt6fpxy8adpx4uzs7hgtclqtqe4wq2sjr8', 'zs1df3t2wpdmsjmzd7pwet67grfgr900fg6hjy97hl9vv0tkf7w5eye2zur6xhhzhfxdceu740g04g', 'zs1g342mavfn70xf5pycn8xwu2zsc4vq3fe8zzg99dfkdsdr39rqszvnuzxdp238d3uaqg96mmrclq', 'zs1gp9egrus6zrjqmea23nae7pzpxdy9jt3w65j8netsrcctl3g9e0lrag4tgvctw3r7rhryj4f4ua', 'zs1jttxxfwurarqw3cmkf94ygfs8skh7xxjf83wuh4awq6zjcee9p804p6aurmxv5q02f2uxc3qm93', 'zs1kqaakh9tw8uj039e9hzkh0444rw533uh8r5http8mfjuakmglqyqrjvd2ertnk8j9jlvvnzne9r', 'zs1lpp9qdtj9tkqjfj64wkcprvp2kflpu8cxz8fgclx80uuu4ajsmslkxkx0fh49cpw2yyhxwk0yzg', 'zs1pd047rvjf6es3ypx6vnl7vgnngmt3mkegn4vjtymfy26rxzp97mpjw5y2gkmyljmk99qs2wa4c0', 'zs1qsr5r8fp9scdz4wt08h4uxnlmefl0dmukuzxzs3f8wx6tk6ged399c9taphhx0asagryjkqgya7', 'zs1r4kfr08dm5yx60yg535dzhnpg8m8s4dl8ap42exkwm6699prpw9zu8sgj6xjd5efmrnj6p4u0an', 'zs1sfj20kyt95sfvll20lraw5q22q2aagw2r3pcdgtlaw4epc9sdwymxzdsge8c84f707ytqpjlnx4', 'zs1stzrmj7rfwr8g0hcsgs7aln0vxwcss6h7qfvzkyf9f6vuupeq6eqhrx7kwa0kmm5dawf7f9j8hr', 'zs1u7sy6prj270ucxwcwh9sh7vr33lnlgs6a95nt4hzgwxwvtcesu240px90822mffc0epf2kjlrjq', 'zs1ued52v4gv83yrguw4gpp4j85mtz9euedmwlszqmldl99cgefz8tz4y60ttjgm4avpqegyhnesns', 'zs1uhnyckurr2x4e8ak6ffqes4ggth8n2vxlzyksd8ufv9zltdhggdnxcx4q2jfn8tuea3f7w4yevs', 'zs1v569xvf7klgw8ylgf0dvtd92dtmu669xcqv05qvxud78nt4zwwcjdz046ghq7lvs4r0d227sgrv', 'zs1xl7l6sn0r5nsa6cex87yp3z7j2mj9jgf32dng6qqp7ym66s8wkx9lykmd896uq62e5xlq2mkaqq', 'zs1yxspuhm7fd5ffrjlmxtsyuryaj983paurt794rg87ln98ekdsugexl7kfvjw97ejhv66ytd3454', 'zs1yyeyhf856aag8vxlq4mkhjs8qjrlxfzz67s4jgjazpedrqfj62tr4zkp3tsj6cgc3hggq4t5raf', 'zs1z4aqxzy2krpvu45mfqes74a20yjmeuxlj0s4x89zr932panatzsfh8wf90qulh5683cgg9q0u2v']; return compromised.indexOf(m) > -1; } }} |
|||
<div id="pageMiner"> |
|||
{{ if (checkCompromised(it.stats.address)) { }} |
|||
<div class="alertbar"> |
|||
Your wallet address was possibly comporomised! Please visit <a href="https://discord.gg/ASMfX7B" target="_blank" rel="noopener noreferrer">discord</a> for more details! |
|||
</div> |
|||
{{ } }} |
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<div class="chartLabel"> |
|||
<div class="workerStatsChartHeaderLeft" title="{{=String(it.stats.address)}}">{{=middleEllipsis(String(it.stats.address).split(".")[0])}}</div> |
|||
<div class="workerStatsChartHeaderRight"><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg">...</span> (Avg)</div> |
|||
<div class="workerStatsChartHeaderRight"><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate">...</span> (Now)</div> |
|||
<div class="workerStatsChartHeaderRight"><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays">...</span> Days</div> |
|||
</div> |
|||
<div class="chartHolder"><svg id="workerHashrate"></svg></div> |
|||
<div class="workerStatsChartFooter"> |
|||
<div title="Lifetime Amount Paid"><i class="fas fa-money-bill-wave fa-fw"></i> Paid: <span id="statsTotalPaid">...</span></div> |
|||
<div title="Accrued Balance Waiting for Min-Payout"><i class="fas fa-piggy-bank fa-fw"></i> Balance: <span id="statsTotalBal">...</span></div> |
|||
<div title="Estimated Balance Waiting for dPoW/Min Confirmations"><i class="far fa-clock fa-fw"></i> Pending: <span id="statsTotalImmature">...</span></div> |
|||
<div title="Current Mining Round Shares"><i class="fas fa-cog fa-fw"></i> Shares: <span id="statsTotalShares">...</span></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="boxesWorkers" class="pure-g boxStats"> </div> |
|||
|
|||
<script> |
|||
var _miner = "{{=String(it.stats.address).split('.')[0]}}"; |
|||
var _workerCount = 0; |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/miner_stats.js'; |
|||
</script> |
|||
</div> |
@ -0,0 +1,25 @@ |
|||
<style> |
|||
#miningKeyPage{ |
|||
margin: 15px; |
|||
} |
|||
#keyFrame{ |
|||
padding: 0; |
|||
border: 0; |
|||
width: 100%; |
|||
height: 750px; |
|||
display: block; |
|||
} |
|||
</style> |
|||
|
|||
<div id="miningKeyPage"> |
|||
|
|||
<p> |
|||
This script run client-side (in your browser). For maximum security <a href="/key.html" download="key.html">download</a> the script and run it locally and |
|||
offline in a modern web browser. |
|||
</p> |
|||
|
|||
<iframe id="keyFrame" src="/key.html"> |
|||
|
|||
</iframe> |
|||
|
|||
</div> |
@ -0,0 +1,68 @@ |
|||
{{ |
|||
function readableDate(a){ |
|||
return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; |
|||
} |
|||
function bigNumber(x){ |
|||
return (x > 1000000000000) ? (x / 1000000000000).toFixed(1) + 'T' : (x > 1000000000) ? (x / 1000000000).toFixed(1) + 'B' : (x > 1000000) ? (x / 1000000).toFixed(1) + 'M' : (x > 1000) ? (x / 1000).toFixed(1) + 'K' : x.toFixed(1); |
|||
} |
|||
function timeTil(x){ |
|||
return (x < 0) ? 'Now' : (x > 86400) ? (x/86400).toFixed(1) + ' Days' : (x > 3600) ? (x/3600).toFixed(1) + ' Hours' : (x > 60) ? (x / 60).toFixed(1) + ' Minutes' : x + ' Seconds'; |
|||
} |
|||
function timeTilNumbers(timestamp) { |
|||
return new Date(timestamp * 1000).toISOString().substr(11, 8); |
|||
} |
|||
}} |
|||
<div id="pagePayments"> |
|||
<div class="alertbar"> |
|||
<!-- Payout frequency has been changed to 4 hours because miners have been mining to exchange addresses…--> |
|||
<!-- Block Reward Halving occured at block <a href="https://explorer.pirate.black/block/00000000004332fdca73719414e6e595c5f9f909bf39df5ff68f56b008b29790" target="_blank" rel="nofollow noreferrer noopener">388,885</a>!! Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 --> |
|||
Payout strategy changed to PPLNT with 1 hour payout frequency! Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 |
|||
</div> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="poolLabel"> |
|||
{{=it.stats.pools[pool].name}} Payments<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<small> |
|||
Lifetime<br class="responsiveonly" /> |
|||
<i class="fas fa-link fa-fw"></i> <span id="statsValidBlocks{{=pool}}">{{=parseInt(it.stats.pools[pool].poolStats.validBlocks).toLocaleString('en')}}</span> Blocks Found<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<i class="fas fa-money-bill-wave fa-fw"></i> <span id="statsTotalPaid{{=pool}}">{{=parseInt(it.stats.pools[pool].poolStats.totalPaid).toLocaleString('en')}}</span> {{=it.stats.pools[pool].symbol}} Paid<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<i class="far fa-clock fa-fw"></i> Next Payment ETA <span id="statsNextPayment{{=pool}}" class="countdownTimer">{{=timeTil(it.poolsConfigs[pool].paymentProcessing.paymentInterval-parseInt((new Date().getTime() - parseInt(it.stats.pools[pool].payments[0].time))/1000))}}</span> |
|||
</small> |
|||
</div> |
|||
<table class="pure-table" id="paymentTable{{=pool}}"> |
|||
<thead> |
|||
<tr> |
|||
<th>Blocks</th> |
|||
<th>Time</th> |
|||
<th>Miners</th> |
|||
<th>Shares</th> |
|||
<th>Amount</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{{ for(var p in it.stats.pools[pool].payments) { }} |
|||
<tr id="payment{{=pool}}{{=it.stats.pools[pool].payments[p].time}}"> |
|||
<td class="paymentblocks{{if (it.stats.pools[pool].payments[p].blocks.length > 9) { }} paymentblocksexpand{{ } }}" title="Blocks:{{=it.stats.pools[pool].payments[p].blocks.length}} {{=it.stats.pools[pool].payments[p].opid}}"> |
|||
<span class="responsiveTableLabel"><i class="fas fa-link fa-fw"></i></span> <span>Blocks: [{{=it.stats.pools[pool].payments[p].blocks.length}}] </span> |
|||
{{if (String(it.stats.pools[pool].name) == "KMD") { }} |
|||
<a href="https://kmdexplorer.io/tx/{{=it.stats.pools[pool].payments[p].txid}}" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (typeof it.stats.pools[pool].payments[p].txid !== 'undefined' && (String(it.stats.pools[pool].name).startsWith("pirate") || String(it.stats.pools[pool].name).startsWith("arrr")) ) { }} |
|||
<a href="https://explorer.pirate.black/tx/{{=it.stats.pools[pool].payments[p].txid}}" target="_blank" rel="noopener noreferrer">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else { }} |
|||
<a>{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } }} |
|||
<div class="fade">▼</div> |
|||
</td> |
|||
<td><span class="responsiveTableLabel"><i class="far fa-clock fa-fw"></i> Time: </span>{{=readableDate(it.stats.pools[pool].payments[p].time)}}</td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-users fa-fw"></i> Miners: </span>{{=it.stats.pools[pool].payments[p].miners}}</td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span>{{=bigNumber(it.stats.pools[pool].payments[p].shares)}}</td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-money-bill-wave fa-fw"></i> Amount: </span>{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</tbody> |
|||
</table> |
|||
{{ } }} |
|||
|
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/payments.js'; |
|||
</script> |
|||
</div> |
@ -0,0 +1,112 @@ |
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }} |
|||
{{ function bigNumber(x){ return (x > 1000000000000) ? (x / 1000000000000).toFixed(1) + 'T' : (x > 1000000000) ? (x / 1000000000).toFixed(1) + 'B' : (x > 1000000) ? (x / 1000000).toFixed(1) + 'M' : (x > 1000) ? (x / 1000).toFixed(1) + 'K' : x.toFixed(1); } }} |
|||
{{ function middleEllipsis(x) { return x.length > 40 ? x.substring(0, 15) + '...' + x.substring(x.length-15, x.length): x; } }} |
|||
<div id="pageStats"> |
|||
<div class="alertbar"> |
|||
<!-- New piratepool.io discord support and discussion channel created! <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">join #piratepool</a> 🐸 --> |
|||
<!-- Payout strategy changed to PPLNT with 1 hour payout frequency! Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 --> |
|||
Miner Privacy Enabled! Search for your miner with Miner Lookup. Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 |
|||
</div> |
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<h1 class="chartLabel">Pool Hashrate</h1> |
|||
<div class="chartHolder"><svg id="poolHashrate"></svg></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-g boxStats"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<div class="l-box"> |
|||
<h3>{{=it.stats.pools[pool].name}} Pool Stats</h3> |
|||
<div class="boxStatsList"> |
|||
<ul> |
|||
<li><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</li> |
|||
<li><i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</li> |
|||
<li><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-u-1-2 pure-responsive-disable"> |
|||
<div class="l-box"> |
|||
<h3>{{=it.stats.pools[pool].name}} Network Stats</h3> |
|||
<div class="boxStatsList"> |
|||
<ul> |
|||
<li><i class="fas fa-link fa-fw" aria-hidden="true"></i> Block Height: <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></li> |
|||
<li><i class="fas fa-tachometer-alt fa-fw"></i> Network Hash/s: <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></li> |
|||
<li><i class="fas fa-unlock-alt fa-fw" aria-hidden="true"></i> Difficulty: <span id="statsNetworkDiff{{=pool}}">{{=bigNumber(it.stats.pools[pool].poolStats.networkDiff)}}</span></li> |
|||
<!-- <li><i class="fas fa-coins fa-fw"></i> Total Supply: <span id="statsNetworkCoins{{=pool}}">{{=(99554304+(it.stats.pools[pool].poolStats.networkBlocks-388884)*128).toLocaleString('en')}}</span></li> --> |
|||
<li><i class="fas fa-coins fa-fw"></i> Total Supply: <span id="statsNetworkCoins{{=pool}}">200,000,000</span></li> |
|||
<li><i class="fas fa-users fa-fw"></i> Node Connections: <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="blocksFound" id="blocksFound{{=it.stats.pools[pool].name}}"> |
|||
<div class="blocksFoundHeader"> |
|||
<div> |
|||
{{=it.stats.pools[pool].name}} Recent Blocks Found (<span id="blocksFoundHeaderCount{{=it.stats.pools[pool].name}}">{{=it.stats.pools[pool].pending.blocks.length}}</span>) |
|||
</div> |
|||
<div> |
|||
Lifetime<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<i class="fas fa-link fa-fw"></i> <span id="statsValidBlocks{{=pool}}">{{=parseInt(it.stats.pools[pool].poolStats.validBlocks).toLocaleString('en')}}</span> Blocks Found<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<i class="fas fa-money-bill-wave fa-fw"></i> <span id="statsTotalPaid{{=pool}}">{{=parseInt(it.stats.pools[pool].poolStats.totalPaid).toLocaleString('en')}}</span> {{=it.stats.pools[pool].symbol}} Paid |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="blocksFoundList"> |
|||
{{ for (var b in it.stats.pools[pool].pending.blocks) { }} |
|||
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }} |
|||
<div class="blocksFoundPending" id="blocksFoundPending{{=block[0]}}" title="{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }}{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }}Waiting for dPoW notarization{{} else if (it.stats.pools[pool].pending.confirms[block[0]] < it.poolsConfigs[pool].paymentProcessing.minConf*2) { }}Waiting for min confirmations{{ } else { }}Queued for payment{{ } }}{{ } else { }}Waiting for payment processor to review{{ } }}"> |
|||
<div> |
|||
<i class="fas fa-link fa-fw"></i> |
|||
<small>Block:</small> |
|||
<a href="https://explorer.pirate.black/block/{{=block[0]}}" target="_blank" rel="noopener noreferrer">{{=block[2]}}</a> |
|||
|
|||
{{if (block[4] != null) { }} |
|||
<span><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }} |
|||
{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }} |
|||
<span class="countLabel">Need dPoW</span> |
|||
{{ } else { }} |
|||
<span class="countLabel">{{=it.stats.pools[pool].pending.confirms[block[0]]}}/{{=it.poolsConfigs[pool].paymentProcessing.minConf*2}}</span> |
|||
{{ } }} |
|||
{{ } else { }} |
|||
<span class="countLabel">Pending</span> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
|
|||
{{ for(var i=0; i < 8; i++) { }} |
|||
{{ var block = it.stats.pools[pool].confirmed.blocks[i].split(":"); }} |
|||
<div class="blocksFoundPaid" id="blocksFoundPaid{{=block[0]}}" title="Payment sent, please check payments page"> |
|||
<div> |
|||
<i class="fas fa-link fa-fw"></i> |
|||
<small>Block:</small> |
|||
<a href="https://explorer.pirate.black/block/{{=block[0]}}" target="_blank" rel="noopener noreferrer">{{=block[2]}}</a> |
|||
{{if (block[4] != null) { }} |
|||
<span><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
<span class="countLabel">Paid</span> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
|
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js'; |
|||
</script> |
|||
</div> |
@ -0,0 +1,30 @@ |
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Pool</th> |
|||
<th>Algo</th> |
|||
<th>Workers</th> |
|||
<th>Valid Shares</th> |
|||
<th>Invalid Shares</th> |
|||
<th>Total Blocks</th> |
|||
<th>Pending</th> |
|||
<th>Confirmed</th> |
|||
<th>Orphaned</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<tr> |
|||
<td>{{=it.stats.pools[pool].name}}</td> |
|||
<td>{{=it.stats.pools[pool].algorithm}}</td> |
|||
<td>{{=Object.keys(it.stats.pools[pool].workers).length}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.invalidShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validBlocks}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.pending}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.confirmed}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.orphaned}}</td> |
|||
<td>{{=it.stats.pools[pool].hashrateString}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
@ -0,0 +1,56 @@ |
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ function bigNumber(x){ return (x > 1000000000000) ? (x / 1000000000000).toFixed(1) + 'T' : (x > 1000000000) ? (x / 1000000000).toFixed(1) + 'B' : (x > 1000000) ? (x / 1000000).toFixed(1) + 'M' : (x > 1000) ? (x / 1000).toFixed(1) + 'K' : x.toFixed(1); } }} |
|||
{{ function middleEllipsis(x) { return x.length > 40 ? x.substring(0, 20) + '...' + x.substring(x.length-20, x.length): x; } }} |
|||
<div id="pageWorkers"> |
|||
<div class="alertbar"> |
|||
Miner Privacy Enabled! Search for your miner with Miner Lookup. Join <a href="https://discord.gg/ASMfX7B" target="_blank" rel="nofollow noreferrer noopener" class="highlight">#piratepool</a> for support and discussion! 🐸 |
|||
</div> |
|||
{{ var i=0; for(var pool in it.stats.pools) { }} |
|||
<div id="workers{{=pool}}"> |
|||
<div class="poolLabel"> |
|||
<span style="float:right;"> |
|||
<small>Miner Lookup: |
|||
<input type="text" class="form-control input-lg" onkeypress="return searchKeyPress(event);" /> |
|||
<span class="input-group-btn"> |
|||
<button class="btn btn-default btn-lg" type="button">Lookup</button> |
|||
</span> |
|||
</small> |
|||
</span> |
|||
{{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners<span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<small> |
|||
<span title="Total number of miners"><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</span><span class="responsivehide">   </span> |
|||
<span title="Total number of workers"><i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</span><span class="responsivehide">   </span><br class="responsiveonly" /> |
|||
<span title="Current Hashrate"><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span></span><span class="responsivehide">   </span> |
|||
<span title="Current round total shares"><i class="fas fa-cog fa-fw"></i> <span id="statsShares{{=pool}}">{{=bigNumber(it.stats.pools[pool].shareCount)}}</span> Shares</span> |
|||
</small> |
|||
</div> |
|||
<table class="pure-table poolMinerTable"> |
|||
<thead> |
|||
<tr> |
|||
<th>Miner</th> |
|||
<th>Shares</th> |
|||
<th>Efficiency</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
{{ var minerindex = 0; }} |
|||
{{ for(var worker in it.stats.pools[pool].miners) { }} |
|||
{{ minerindex++; }} |
|||
{{var workerstat = it.stats.pools[pool].miners[worker];}} |
|||
<tr id="miner-{{=minerindex}}" data-hashrate="{{=workerstat.hashrate}}"> |
|||
<td><span class="responsiveTableLabel"><i class="far fa-address-card fa-fw"></i></span>Miner #{{=minerindex}}</td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span><span>{{=bigNumber(workerstat.currRoundShares)}}</span></td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-bullseye fa-fw"></i> Efficiency: </span><span>{{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}}</span></td> |
|||
<td><span class="responsiveTableLabel"><i class="fas fa-tachometer-alt fa-fw"></i> Hashrate: </span><span>{{=workerstat.hashrateString}}</span></td> |
|||
</tr> |
|||
{{ } }} |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
{{ } }} |
|||
|
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/workers.js'; |
|||
</script> |
|||
</div> |
@ -0,0 +1,100 @@ |
|||
var docCookies = { |
|||
getItem: function (sKey) { |
|||
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; |
|||
}, |
|||
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { |
|||
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } |
|||
var sExpires = ""; |
|||
if (vEnd) { |
|||
switch (vEnd.constructor) { |
|||
case Number: |
|||
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; |
|||
break; |
|||
case String: |
|||
sExpires = "; expires=" + vEnd; |
|||
break; |
|||
case Date: |
|||
sExpires = "; expires=" + vEnd.toUTCString(); |
|||
break; |
|||
} |
|||
} |
|||
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); |
|||
return true; |
|||
}, |
|||
removeItem: function (sKey, sPath, sDomain) { |
|||
if (!sKey || !this.hasItem(sKey)) { return false; } |
|||
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); |
|||
return true; |
|||
}, |
|||
hasItem: function (sKey) { |
|||
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); |
|||
} |
|||
}; |
|||
|
|||
var password = docCookies.getItem('password'); |
|||
|
|||
|
|||
function showLogin(){ |
|||
$('#adminCenter').hide(); |
|||
$('#passwordForm').show(); |
|||
} |
|||
|
|||
function showAdminCenter(){ |
|||
$('#passwordForm').hide(); |
|||
$('#adminCenter').show(); |
|||
} |
|||
|
|||
function tryLogin(){ |
|||
apiRequest('pools', {}, function(response){ |
|||
showAdminCenter(); |
|||
displayMenu(response.result) |
|||
}); |
|||
} |
|||
|
|||
function displayMenu(pools){ |
|||
$('#poolList').after(Object.keys(pools).map(function(poolName){ |
|||
return '<li class="poolMenuItem"><a href="#">' + poolName + '</a></li>'; |
|||
}).join('')); |
|||
} |
|||
|
|||
function apiRequest(func, data, callback){ |
|||
var httpRequest = new XMLHttpRequest(); |
|||
httpRequest.onreadystatechange = function(){ |
|||
if (httpRequest.readyState === 4 && httpRequest.responseText){ |
|||
if (httpRequest.status === 401){ |
|||
docCookies.removeItem('password'); |
|||
$('#password').val(''); |
|||
showLogin(); |
|||
alert('Incorrect Password'); |
|||
} |
|||
else{ |
|||
var response = JSON.parse(httpRequest.responseText); |
|||
callback(response); |
|||
} |
|||
} |
|||
}; |
|||
httpRequest.open('POST', '/api/admin/' + func); |
|||
data.password = password; |
|||
httpRequest.setRequestHeader('Content-Type', 'application/json'); |
|||
httpRequest.send(JSON.stringify(data)); |
|||
} |
|||
|
|||
if (password){ |
|||
tryLogin(); |
|||
} |
|||
else{ |
|||
showLogin(); |
|||
} |
|||
|
|||
$('#passwordForm').submit(function(event){ |
|||
event.preventDefault(); |
|||
password = $('#password').val(); |
|||
if (password){ |
|||
if ($('#remember').is(':checked')) |
|||
docCookies.setItem('password', password, Infinity); |
|||
else |
|||
docCookies.setItem('password', password); |
|||
tryLogin(); |
|||
} |
|||
return false; |
|||
}); |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
@ -0,0 +1,2 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<browserconfig><msapplication><tile><square70x70logo src="/static/favicon/ms-icon-70x70.png"/><square150x150logo src="/static/favicon/ms-icon-150x150.png"/><square310x310logo src="/static/favicon/ms-icon-310x310.png"/><TileColor>#BB9645</TileColor></tile></msapplication></browserconfig> |
After Width: | Height: | Size: 641 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,41 @@ |
|||
{ |
|||
"name": "App", |
|||
"icons": [ |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-36x36.png", |
|||
"sizes": "36x36", |
|||
"type": "image\/png", |
|||
"density": "0.75" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-48x48.png", |
|||
"sizes": "48x48", |
|||
"type": "image\/png", |
|||
"density": "1.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-72x72.png", |
|||
"sizes": "72x72", |
|||
"type": "image\/png", |
|||
"density": "1.5" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-96x96.png", |
|||
"sizes": "96x96", |
|||
"type": "image\/png", |
|||
"density": "2.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-144x144.png", |
|||
"sizes": "144x144", |
|||
"type": "image\/png", |
|||
"density": "3.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-192x192.png", |
|||
"sizes": "192x192", |
|||
"type": "image\/png", |
|||
"density": "4.0" |
|||
} |
|||
] |
|||
} |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,3 @@ |
|||
$(function() { |
|||
initStatData(); |
|||
}); |
After Width: | Height: | Size: 797 B |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,208 @@ |
|||
//Set this to match config for payment countdown
|
|||
var explorerURL = 'https://explorer.pirate.black/'; |
|||
var minConfirmations = 10; |
|||
var paymentInterval = 14400; |
|||
|
|||
var nextPaymentTimer; |
|||
var paymentTimerOn = false; |
|||
|
|||
var poolHashrateData; |
|||
var poolHashrateChart; |
|||
var lastHashrateUpdate = {}; |
|||
|
|||
var statData; |
|||
var poolKeys; |
|||
|
|||
$(function(){ |
|||
initStatData(); |
|||
|
|||
var hotSwap = function(page, pushSate){ |
|||
if (pushSate) history.pushState(null, null, '/' + page); |
|||
$('.pure-menu-selected').removeClass('pure-menu-selected'); |
|||
$('a[href="/' + page + '"]').parent().addClass('pure-menu-selected'); |
|||
$.get("/get_page", {id: page}, function(data){ |
|||
$('main').html(data); |
|||
}, 'html') |
|||
}; |
|||
|
|||
$('body').on('click', '.hot-swapper', function(event) { |
|||
if (event.which !== 1) return; |
|||
var pageId = $(this).attr('href').slice(1); |
|||
hotSwap(pageId, true); |
|||
event.preventDefault(); |
|||
return false; |
|||
}); |
|||
|
|||
window.addEventListener('load', function() { |
|||
setTimeout(function() { |
|||
window.addEventListener("popstate", function(e) { |
|||
if (location.hash.length == 0) { |
|||
hotSwap(location.pathname.slice(1)); |
|||
} |
|||
}); |
|||
}, 0); |
|||
}); |
|||
|
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
var stats = JSON.parse(e.data); |
|||
var statpush = {}; |
|||
//Slim down the data we save to statData
|
|||
for (var pool in stats.pools) { |
|||
statpush[pool] = {hashrate: stats.pools[pool].hashrate, workerCount: stats.pools[pool].workerCount, blocks: stats.pools[pool].blocks}; |
|||
} |
|||
statData.push({time:stats.time, pools: statpush}); |
|||
statData.shift(); // remove old unused record
|
|||
|
|||
for (var pool in stats.pools) { |
|||
$('#statsMiners' + pool).text(stats.pools[pool].minerCount); |
|||
$('#statsWorkers' + pool).text(stats.pools[pool].workerCount); |
|||
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays); |
|||
$('#statsValidBlocks' + pool).text(parseInt(stats.pools[pool].poolStats.validBlocks).toLocaleString('en')); |
|||
$('#statsTotalPaid' + pool).text(parseInt(stats.pools[pool].poolStats.totalPaid).toLocaleString('en')); |
|||
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks); |
|||
$('#statsNetworkDiff' + pool).text(bigNumber(stats.pools[pool].poolStats.networkDiff)); |
|||
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols)); |
|||
$('#statsNetworkConnections' + pool).text(stats.pools[pool].poolStats.networkConnections); |
|||
} |
|||
|
|||
for (algo in stats.algos) { |
|||
$('#statsMiners' + algo).text(stats.algos[algo].workers); |
|||
$('#statsHashrate' + algo).text(stats.algos[algo].hashrateString); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function initStatData(callback) { |
|||
clearInterval(nextPaymentTimer); |
|||
paymentTimerOn = false; |
|||
document.querySelectorAll('.nvtooltip').forEach(function(element) { |
|||
element.remove(); |
|||
}); |
|||
if ( statData === undefined || statData.length == 0 ) { |
|||
$.getJSON('/api/pool_stats', function(data){ |
|||
statData = data; |
|||
buildChartData(); |
|||
if (typeof callback === "function") { |
|||
callback(); |
|||
} |
|||
}); |
|||
} else { |
|||
buildChartData(); |
|||
if (typeof callback === "function") { |
|||
callback(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
function bigNumber(x) { |
|||
return (x > 1000000000000) ? (x / 1000000000000).toFixed(1) + 'T' : (x > 1000000000) ? (x / 1000000000).toFixed(1) + 'B' : (x > 1000000) ? (x / 1000000).toFixed(1) + 'M' : (x > 1000) ? (x / 1000).toFixed(1) + 'K' : x.toFixed(1); |
|||
} |
|||
|
|||
function buildChartData() { |
|||
var pools = {}; |
|||
|
|||
poolKeys = []; |
|||
for (var i = 0; i < statData.length; i++){ |
|||
for (var pool in statData[i].pools){ |
|||
if (poolKeys.indexOf(pool) === -1) { |
|||
poolKeys.push(pool); |
|||
lastHashrateUpdate[pool] = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < statData.length; i++) { |
|||
var time = statData[i].time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++){ |
|||
var pName = poolKeys[f]; |
|||
var a = pools[pName] = (pools[pName] || { |
|||
hashrate: [] |
|||
}); |
|||
if (pName in statData[i].pools){ |
|||
a.hashrate.push([time, statData[i].pools[pName].hashrate]); |
|||
} else { |
|||
a.hashrate.push([time, 0]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
poolHashrateData = []; |
|||
for (var pool in pools){ |
|||
poolHashrateData.push({ |
|||
key: pool, |
|||
values: pools[pool].hashrate |
|||
}); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
} |
|||
} |
|||
|
|||
function calculateAverageHashrate(pool) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < poolHashrateData[i].values.length; ii++) { |
|||
if (pool == null || poolHashrateData[i].key === pool) { |
|||
count++; |
|||
avg += parseFloat(poolHashrateData[i].values[ii][1]); |
|||
} |
|||
} |
|||
if (count > total) |
|||
total = count; |
|||
} |
|||
avg = avg / total; |
|||
return avg; |
|||
} |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
return hashrate.toFixed(2) + byteUnits[i]; |
|||
} |
|||
|
|||
function getReadableNetworkHashRateString(hashrate){ |
|||
hashrate = (hashrate * 1000000); |
|||
if (hashrate < 1000000) |
|||
return '0 Sol'; |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
return hashrate.toFixed(2) + byteUnits[i]; |
|||
} |
|||
|
|||
function middleEllipsis(x, cutlength) { |
|||
if (typeof cutlength == 'undefined') { |
|||
var cutlength=15; |
|||
} |
|||
return x.length > 40 ? x.substring(0, cutlength) + '...' + x.substring(x.length-cutlength, x.length): x; |
|||
} |
|||
|
|||
function readableDate(a){ |
|||
return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; |
|||
} |
|||
|
|||
function timeOfDayFormat(timestamp){ |
|||
return new Date(parseInt(timestamp)).toISOString().substring(11, 16); |
|||
} |
|||
|
|||
function timeTil(timestamp) { |
|||
return (timestamp > 86400) ? (timestamp/86400).toFixed(1) + ' Days' : (timestamp > 3600) ? (timestamp/3600).toFixed(1) + ' Hours' : (timestamp > 60) ? (timestamp / 60).toFixed(1) + ' Minutes' : timestamp + ' Seconds'; |
|||
} |
|||
|
|||
function timeTilNumbers(timestamp) { |
|||
return new Date(timestamp * 1000).toISOString().substr(11, 8); |
|||
} |
|||
|
|||
function zeroOrGreater(value) { |
|||
return (value < 0) ? 0 : value; |
|||
} |
@ -0,0 +1,237 @@ |
|||
var workerHashrateData; |
|||
var workerHashrateChart; |
|||
var workerHistoryMax = 160; |
|||
|
|||
var minerStatData; |
|||
var totalHash; |
|||
var totalImmature; |
|||
var totalBal; |
|||
var totalPaid; |
|||
var totalShares; |
|||
|
|||
$(function() { |
|||
// resize chart on window resize
|
|||
nv.utils.windowResize(triggerMinerChartUpdates); |
|||
|
|||
// grab initial stats
|
|||
$.getJSON('/api/worker_stats?'+_miner, function(data){ |
|||
minerStatData = data; |
|||
for (var w in minerStatData.workers) { _workerCount++; } |
|||
buildMinerChartData(); |
|||
displayMinerCharts(); |
|||
rebuildWorkerDisplay(); |
|||
updateMinerStats(); |
|||
}); |
|||
|
|||
// live stat updates
|
|||
statsSource.addEventListener('message', function(e){ |
|||
if (document.querySelector('#pageMiner') !== null) { |
|||
// 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){ |
|||
minerStatData = data; |
|||
// check for missing workers
|
|||
var wc = 0; |
|||
var rebuilt = false; |
|||
// update worker stats
|
|||
for (var w in minerStatData.workers) { wc++; } |
|||
// TODO, this isn't 100% fool proof!
|
|||
if (_workerCount != wc) { |
|||
if (_workerCount > wc) { |
|||
rebuildWorkerDisplay(); |
|||
rebuilt = true; |
|||
} |
|||
_workerCount = wc; |
|||
} |
|||
rebuilt = (rebuilt || updateMinerChartData()); |
|||
updateMinerStats(); |
|||
if (!rebuilt) { |
|||
updateWorkerStats(); |
|||
} |
|||
}); |
|||
|
|||
var stats = JSON.parse(e.data); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
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 buildMinerChartData(){ |
|||
var workers = {}; |
|||
for (var w in minerStatData.history) { |
|||
var worker = getWorkerNameFromAddress(w); |
|||
var a = workers[worker] = (workers[worker] || { |
|||
hashrate: [] |
|||
}); |
|||
for (var wh in minerStatData.history[w]) { |
|||
a.hashrate.push([minerStatData.history[w][wh].time * 1000, minerStatData.history[w][wh].hashrate]); |
|||
} |
|||
if (a.hashrate.length > workerHistoryMax) { |
|||
workerHistoryMax = a.hashrate.length; |
|||
} |
|||
} |
|||
|
|||
var i=0; |
|||
workerHashrateData = []; |
|||
for (var worker in workers){ |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
//disabled: (i > Math.min((_workerCount-1), 3)),
|
|||
disabled: false, |
|||
values: workers[worker].hashrate |
|||
}); |
|||
i++; |
|||
} |
|||
} |
|||
|
|||
function updateMinerChartData(){ |
|||
var workers = {}; |
|||
for (var w in minerStatData.history) { |
|||
var worker = getWorkerNameFromAddress(w); |
|||
// get a reference to lastest workerhistory
|
|||
for (var wh in minerStatData.history[w]) { } |
|||
//var wh = minerStatData.history[w][minerStatData.history[w].length - 1];
|
|||
var foundWorker = false; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
if (workerHashrateData[i].key === worker) { |
|||
foundWorker = true; |
|||
if (workerHashrateData[i].values.length >= workerHistoryMax) { |
|||
workerHashrateData[i].values.shift(); |
|||
} |
|||
workerHashrateData[i].values.push([minerStatData.history[w][wh].time * 1000, minerStatData.history[w][wh].hashrate]); |
|||
break; |
|||
} |
|||
} |
|||
if (!foundWorker) { |
|||
var hashrate = []; |
|||
hashrate.push([minerStatData.history[w][wh].time * 1000, minerStatData.history[w][wh].hashrate]); |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
values: hashrate |
|||
}); |
|||
rebuildWorkerDisplay(); |
|||
return true; |
|||
} |
|||
} |
|||
triggerMinerChartUpdates(); |
|||
return false; |
|||
} |
|||
|
|||
function calculateAverageMinerHashrate(worker) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) { |
|||
if (worker == null || workerHashrateData[i].key === worker) { |
|||
count++; |
|||
avg += parseFloat(workerHashrateData[i].values[ii][1]); |
|||
} |
|||
} |
|||
if (count > total) |
|||
total = count; |
|||
} |
|||
avg = avg / total; |
|||
return avg; |
|||
} |
|||
|
|||
function triggerMinerChartUpdates(){ |
|||
workerHashrateChart.update(); |
|||
} |
|||
|
|||
function displayMinerCharts() { |
|||
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 updateMinerStats() { |
|||
totalHash = minerStatData.totalHash; |
|||
totalPaid = minerStatData.paid; |
|||
totalBal = minerStatData.balance; |
|||
totalImmature = minerStatData.immature; |
|||
totalShares = minerStatData.totalShares; |
|||
// do some calculations
|
|||
var _blocktime = 250; |
|||
var _networkHashRate = parseFloat(minerStatData.networkSols) * 1.2; |
|||
var _myHashRate = (totalHash / 1000000) * 2; |
|||
var luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3); |
|||
// update miner stats
|
|||
$("#statsHashrate").text(getReadableHashRateString(totalHash)); |
|||
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageMinerHashrate(null))); |
|||
$("#statsLuckDays").text(luckDays); |
|||
$("#statsTotalImmature").text(totalImmature); |
|||
$("#statsTotalBal").text(totalBal); |
|||
$("#statsTotalPaid").text(totalPaid); |
|||
$("#statsTotalShares").text(bigNumber(totalShares)); |
|||
} |
|||
|
|||
function updateWorkerStats() { |
|||
// update worker stats
|
|||
var i=0; |
|||
for (var w in minerStatData.workers) { i++; |
|||
var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, ''); |
|||
var saneWorkerName = getWorkerNameFromAddress(w); |
|||
$("#statsHashrate"+htmlSafeWorkerName).text(getReadableHashRateString(minerStatData.workers[w].hashrate)); |
|||
$("#statsHashrateAvg"+htmlSafeWorkerName).text(getReadableHashRateString(calculateAverageMinerHashrate(saneWorkerName))); |
|||
$("#statsLuckDays"+htmlSafeWorkerName).text(minerStatData.workers[w].luckDays); |
|||
$("#statsPaid"+htmlSafeWorkerName).text(minerStatData.workers[w].paid); |
|||
$("#statsBalance"+htmlSafeWorkerName).text(minerStatData.workers[w].balance); |
|||
$("#statsShares"+htmlSafeWorkerName).text(bigNumber(minerStatData.workers[w].currRoundShares)); |
|||
$("#statsDiff"+htmlSafeWorkerName).text(bigNumber(zeroOrGreater(minerStatData.workers[w].diff))); |
|||
} |
|||
} |
|||
|
|||
function addWorkerToDisplay(name, htmlSafeName, workerObj) { |
|||
var htmlToAdd = ""; |
|||
htmlToAdd ='<div class="pure-u-1-4 pure-responsive-disable"><div class="l-box">'; |
|||
htmlToAdd+='<h3 class="boxLowerHeader">'; |
|||
htmlToAdd+=(htmlSafeName.indexOf("_") >= 0) ? htmlSafeName.substr(htmlSafeName.indexOf("_")+1,htmlSafeName.length) : 'noname'; |
|||
htmlToAdd+='</h3>'; |
|||
htmlToAdd+='<div class="boxStatsList"><ul>'; |
|||
htmlToAdd+='<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</li>'; |
|||
htmlToAdd+='<li><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageMinerHashrate(name))+'</span> (Avg)</li>'; |
|||
htmlToAdd+='<li><i class="fas fa-unlock-alt fa-fw"></i> Diff: <span id="statsDiff'+htmlSafeName+'">'+bigNumber(zeroOrGreater(workerObj.diff))+'</span></li>'; |
|||
htmlToAdd+='<li><i class="fas fa-cog fa-fw"></i> Shares: <span id="statsShares'+htmlSafeName+'">'+bigNumber(workerObj.currRoundShares)+'</span></li>'; |
|||
htmlToAdd+='<li><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays'+htmlSafeName+'">'+workerObj.luckDays+'</span> Days</li>'; |
|||
htmlToAdd+='<li><i class="fas fa-piggy-bank fa-fw"></i> Bal: <span id="statsBalance'+htmlSafeName+'">'+workerObj.balance+'</span></li>'; |
|||
htmlToAdd+='<li><i class="fas fa-money-bill-wave fa-fw"></i> Paid: <span id="statsPaid'+htmlSafeName+'">'+workerObj.paid+'</span></li>'; |
|||
htmlToAdd+='</ul></div></div></div>'; |
|||
$("#boxesWorkers").html($("#boxesWorkers").html()+htmlToAdd); |
|||
} |
|||
|
|||
function rebuildWorkerDisplay() { |
|||
$("#boxesWorkers").html(""); |
|||
var i=0; |
|||
for (var w in minerStatData.workers) { i++; |
|||
var htmlSafeWorkerName = w.split('.').join('_').replace(/[^\w\s]/gi, ''); |
|||
var saneWorkerName = getWorkerNameFromAddress(w); |
|||
addWorkerToDisplay(saneWorkerName, htmlSafeWorkerName, minerStatData.workers[w]); |
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
$(function() { |
|||
initStatData(); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
if (document.querySelector('#pagePayments') !== null) { |
|||
var stats = JSON.parse(e.data); |
|||
|
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
|
|||
for (var i = 0; i < stats.pools[pool].payments.length; i++) { |
|||
var paymentstat = stats.pools[pool].payments[i]; |
|||
|
|||
var existingRow = document.querySelector('#payment' + pool + paymentstat.time); |
|||
|
|||
if (existingRow == null) { |
|||
clearInterval(nextPaymentTimer); |
|||
paymentTimerOn = false; |
|||
|
|||
//Add new
|
|||
var insertPaymentTr = document.createElement('tr'); |
|||
insertPaymentTr.id = 'payment' + pool + paymentstat.time; |
|||
insertPaymentTr.setAttribute('class', 'dynamicallyInserted'); |
|||
insertPaymentTr.style.opacity = 0; |
|||
insertPaymentTr.style.transition = 'opacity 1s ease-in'; |
|||
|
|||
if (typeof paymentstat.txid !== 'undefined') { |
|||
var explorerlink = '<a href="' + explorerURL + 'tx/' + paymentstat.txid + '" target="_blank" rel="noopener noreferrer">' + paymentstat.blocks + '</a>'; |
|||
} else { |
|||
var explorerlink = '<a>' + paymentstat.blocks + '</a>'; |
|||
} |
|||
|
|||
insertPaymentTr.innerHTML = '<td class="paymentblocks" title="Blocks:' + paymentstat.blocks.length + ' ' + paymentstat.opid + '">' |
|||
+ '<span class="responsiveTableLabel"><i class="fas fa-link fa-fw"></i></span> <span>Blocks: [' + paymentstat.blocks.length + '] </span>' |
|||
+ explorerlink + '<div class="fade">▼</div></td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="far fa-clock fa-fw"></i> Time: </span>' + readableDate(paymentstat.time) + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-users fa-fw"></i> Miners: </span>' + paymentstat.miners + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span>' + bigNumber(paymentstat.shares) + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-money-bill-wave fa-fw"></i> Amount: </span>' + paymentstat.paid + ' ' + stats.pools[pool].symbol + '</td>'; |
|||
|
|||
var paymentTable = document.querySelector('#paymentTable' + pool + ' tbody'); |
|||
if (paymentTable != null) { |
|||
paymentTable.insertBefore(insertPaymentTr, paymentTable.firstChild); |
|||
setTimeout(() => { |
|||
document.querySelectorAll('.dynamicallyInserted').forEach(function(newPayment) { |
|||
newPayment.style.opacity = 1; |
|||
}); |
|||
}, 25); |
|||
console.log('Added new payment!'); |
|||
} |
|||
} else { |
|||
//Update existing (txid) for private chains
|
|||
if (typeof paymentstat.txid !== 'undefined' && (String(stats.pools[pool].name).startsWith("pirate") || String(stats.pools[pool].name).startsWith("arrr")) ) { |
|||
var paymentblock = document.querySelector('#payment' + pool + paymentstat.time + ' .paymentblocks a'); |
|||
|
|||
paymentblock.setAttribute('href', explorerURL + 'tx/' + paymentstat.txid); |
|||
paymentblock.setAttribute('target', '_blank'); |
|||
paymentblock.setAttribute('rel', 'noopener noreferrer'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Global var from main.js - cleared on initStatData(), this needs to be started after the loop in case a new payment was added and old timer needs to be reset
|
|||
if (!paymentTimerOn) { |
|||
nextPaymentTimer = setInterval(function() { |
|||
var timeElement = document.querySelector('#statsNextPayment' + pool); |
|||
if (timeElement !== null) { |
|||
var timeleft=(paymentInterval-parseInt((new Date().getTime() - parseInt(stats.pools[pool].payments[0].time))/1000)); |
|||
if (timeleft > 0) { |
|||
timeElement.innerHTML = timeTil(timeleft); |
|||
timeElement.setAttribute('title', timeTilNumbers(timeleft)); |
|||
} else { |
|||
timeElement.innerHTML = 'Now'; |
|||
timeElement.setAttribute('title', '00:00:00'); |
|||
} |
|||
} |
|||
}, 1000); |
|||
paymentTimerOn = true; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,149 @@ |
|||
$(function() { |
|||
initStatData(displayCharts); |
|||
|
|||
nv.utils.windowResize(triggerChartUpdates); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
if (document.querySelector('#pageStats') !== null) { |
|||
var stats = JSON.parse(e.data); |
|||
|
|||
var newPoolAdded = (function(){ |
|||
for (var p in stats.pools){ |
|||
if (poolKeys.indexOf(p) === -1) |
|||
return true; |
|||
} |
|||
return false; |
|||
})(); |
|||
|
|||
if (newPoolAdded || Object.keys(stats.pools).length > poolKeys.length) { |
|||
buildChartData(); |
|||
displayCharts(); |
|||
} else { |
|||
var time = stats.time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
if (poolHashrateData[i].key === pool) { |
|||
if (time > lastHashrateUpdate[pool]) { |
|||
poolHashrateData[i].values.push([time, pool in stats.pools ? stats.pools[pool].hashrate : 0]); |
|||
poolHashrateData[i].values.shift(); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
lastHashrateUpdate[pool] = time; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* Handle new found blocks */ |
|||
var poolFoundList = document.querySelector('#blocksFound' + pool + ' .blocksFoundList'); |
|||
if (poolFoundList != null) { |
|||
var pendingblocks = stats.pools[pool].pending.blocks; |
|||
var confirmedblocks = stats.pools[pool].confirmed.blocks; |
|||
var legitpending = []; |
|||
var legitconfirmed = []; |
|||
var prevHeight = 0; |
|||
|
|||
document.querySelector('#blocksFoundHeaderCount' + pool).innerHTML = pendingblocks.length; |
|||
|
|||
//Add new pending blocks
|
|||
//@todo list starts from the heighest to lowest, if multiple blocks found in the same tick it inserts them backwards
|
|||
for ( var block in pendingblocks ) { |
|||
var checkblock = pendingblocks[block].split(':'); |
|||
legitpending.push(checkblock[0]); |
|||
var pendingBlockElement = document.querySelector('#blocksFoundPending' + checkblock[0]); |
|||
if (pendingBlockElement == null) { |
|||
var insertPendingBlock = document.createElement('div'); |
|||
insertPendingBlock.id = 'blocksFoundPending' + checkblock[0]; |
|||
insertPendingBlock.setAttribute('class', 'blocksFoundPending dynamicallyInserted'); |
|||
insertPendingBlock.setAttribute('title', 'Waiting for payment processor to review'); |
|||
insertPendingBlock.style.opacity = 0; |
|||
insertPendingBlock.style.transition = 'opacity 1s ease-in'; |
|||
insertPendingBlock.innerHTML = '<div><i class="fas fa-link fa-fw"></i> <small>Block:</small> <a href="' + explorerURL + 'block/' + checkblock[0] + '" target="_blank" rel="noopener noreferrer">' + checkblock[2] + '</a><span style="padding-left: 10px;"><small>' + readableDate(checkblock[4]) + '</small></span><span class="countLabel">Pending</span></div>'; |
|||
|
|||
if (parseInt(checkblock[2]) > prevHeight) { |
|||
poolFoundList.insertBefore(insertPendingBlock, poolFoundList.firstChild); |
|||
} else { |
|||
poolFoundList.insertBefore(insertPendingBlock, poolFoundList.firstChild.nextSibling); |
|||
} |
|||
prevHeight = parseInt(checkblock[2]); |
|||
setTimeout(() => { |
|||
document.querySelectorAll('.dynamicallyInserted').forEach(function(newBlock) { |
|||
newBlock.style.opacity = 1; |
|||
}); |
|||
}, 25); |
|||
} |
|||
} |
|||
|
|||
//Add new confirmed blocks
|
|||
//@todo simplify duplicate logic
|
|||
for ( var i = 7; i >= 0; i-- ) { |
|||
var checkblock = confirmedblocks[i].split(':'); |
|||
legitconfirmed.push(checkblock[0]); |
|||
var confirmedBlockElement = document.querySelector('#blocksFoundPaid' + checkblock[0]); |
|||
if (confirmedBlockElement == null) { |
|||
var insertPendingBlock = document.createElement('div'); |
|||
insertPendingBlock.id = 'blocksFoundPaid' + checkblock[0]; |
|||
insertPendingBlock.setAttribute('class', 'blocksFoundPaid'); |
|||
insertPendingBlock.setAttribute('title', 'Payment sent, please check payments page'); |
|||
insertPendingBlock.innerHTML = '<div><i class="fas fa-link fa-fw"></i> <small>Block:</small> <a href="' + explorerURL + 'block/' + checkblock[0] + '" target="_blank" rel="noopener noreferrer">' + checkblock[2] + '</a><span style="padding-left: 10px;"><small>' + readableDate(checkblock[4]) + '</small></span><span class="countLabel">Pending</span></div>'; |
|||
|
|||
poolFoundList.insertBefore(insertPendingBlock, document.querySelectorAll('.blocksFoundList .blocksFoundPaid')[0]); |
|||
} |
|||
} |
|||
|
|||
//Update confirms
|
|||
var confirmblocks = stats.pools[pool].pending.confirms; |
|||
for ( var confirm in confirmblocks ) { |
|||
var labelElement = document.querySelector('#blocksFoundPending' + confirm + ' .countLabel'); |
|||
labelElement.innerHTML = (confirmblocks[confirm] == "1") ? 'Need dPoW' : confirmblocks[confirm] + "/" + minConfirmations; |
|||
} |
|||
|
|||
//Remove blocks no longer in pending/confirmed list
|
|||
//Reselect found list because of possible inserts above
|
|||
//Loop in reverse so there's no "confusion"
|
|||
var poolFoundListChildren = document.querySelectorAll('#blocksFound' + pool + ' .blocksFoundList > div'); |
|||
for ( var i = poolFoundListChildren.length-1; i >= 0; i--) { |
|||
var blockDivID = poolFoundListChildren[i].id |
|||
var blockDivCheckPending = blockDivID.replace('blocksFoundPending', ''); |
|||
var blockDivCheckPaid = blockDivID.replace('blocksFoundPaid', ''); |
|||
var blockDivElement = document.getElementById(blockDivID); |
|||
|
|||
if (blockDivElement.classList.contains("blocksFoundPending") && legitpending.indexOf(blockDivCheckPending) < 0) { |
|||
document.getElementById(blockDivID).parentNode.removeChild(document.getElementById(blockDivID)); |
|||
} |
|||
|
|||
if (blockDivElement.classList.contains("blocksFoundPaid") && ( legitconfirmed.indexOf(blockDivCheckPaid) < 0 || legitconfirmed.indexOf(blockDivCheckPaid) > 7 ) ) { |
|||
document.getElementById(blockDivID).parentNode.removeChild(document.getElementById(blockDivID)); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
triggerChartUpdates(); |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function displayCharts(){ |
|||
nv.addGraph(function() { |
|||
poolHashrateChart = nv.models.lineChart() |
|||
.margin({top:0, right: 30, bottom:15, left: 80}) |
|||
.x(function(d){ return d[0] }) |
|||
.y(function(d){ return d[1] }) |
|||
.useInteractiveGuideline(true); |
|||
|
|||
poolHashrateChart.xAxis.tickFormat(timeOfDayFormat); |
|||
|
|||
poolHashrateChart.yAxis.tickFormat(function(d){ |
|||
return getReadableHashRateString(d); |
|||
}); |
|||
|
|||
d3.select('#poolHashrate').datum(poolHashrateData).call(poolHashrateChart); |
|||
|
|||
return poolHashrateChart; |
|||
}); |
|||
} |
|||
|
|||
function triggerChartUpdates(){ |
|||
poolHashrateChart.update(); |
|||
} |
@ -0,0 +1,557 @@ |
|||
:root { |
|||
--gold: #BB9645; |
|||
--blue: #082DB2; |
|||
--grey: #2f2f2f; |
|||
--dark-blue: #071642; |
|||
--dark-grey: #141414; |
|||
--light-grey: #6d6d6d; |
|||
--blood: #690015; |
|||
} |
|||
|
|||
* { |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
html, body, button, input, select, textarea, |
|||
.pure-g [class *= "pure-u"] { |
|||
font-family: 'Roboto', sans-serif; |
|||
line-height: 1.4em; |
|||
} |
|||
|
|||
footer, |
|||
h1, h2, h3, h4, h5, h6, h7, |
|||
.home-menu, |
|||
.chartLabel, |
|||
.blocksFoundHeader, |
|||
.poolLabel, |
|||
.coinInfoHeader, |
|||
.workerStatsChartFooter, |
|||
.nv-axis text, |
|||
.countLabel { |
|||
font-family: 'Bai Jamjuree', sans-serif; |
|||
line-height:1em; |
|||
} |
|||
|
|||
body { |
|||
background-color:#000; |
|||
display: flex; |
|||
flex-direction: column; |
|||
max-width: 1160px; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
header { |
|||
margin-top:5px; |
|||
} |
|||
|
|||
main { |
|||
background-color: var(--dark-grey); |
|||
color:#FFF; |
|||
position: relative; |
|||
} |
|||
|
|||
a, a:link, a:visited { |
|||
color:#FFF; |
|||
} |
|||
|
|||
a:hover { |
|||
color:var(--gold); |
|||
} |
|||
|
|||
code { |
|||
font-family: monospace, serif; |
|||
display:block; |
|||
margin:15px; |
|||
padding:20px; |
|||
background-color: var(--dark-blue); |
|||
word-break:break-all; |
|||
box-shadow: -1px -1px 0 #000; |
|||
} |
|||
|
|||
input { |
|||
color:#000; |
|||
} |
|||
|
|||
button { |
|||
color:#FFF; |
|||
background-color: var(--gold); |
|||
border:none; |
|||
border-radius: 2px; |
|||
padding: 3px 10px; |
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
footer{ |
|||
text-align: center; |
|||
color: #FFF; |
|||
text-decoration: none; |
|||
font-size: 0.8em; |
|||
padding: 15px; |
|||
line-height: 24px; |
|||
} |
|||
|
|||
footer a { |
|||
color: #fff; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
.highlight { |
|||
color:var(--gold); |
|||
font-weight:900; |
|||
} |
|||
|
|||
.muted { |
|||
font-size:0.8em; |
|||
color:var(--light-grey); |
|||
} |
|||
|
|||
.textshadow { |
|||
text-shadow: -3px -3px 0px #000; |
|||
} |
|||
|
|||
br.responsiveonly { |
|||
display:none; |
|||
} |
|||
|
|||
.alertbar { |
|||
color:var(--gold); |
|||
font-weight:900; |
|||
text-shadow: -3px -3px 0px #000; |
|||
text-align:center; |
|||
padding:10px 0; |
|||
background-color:var(--dark-blue); |
|||
} |
|||
|
|||
/* Menus */ |
|||
header .home-menu, |
|||
header .pure-menu { |
|||
background:transparent; |
|||
padding: 0; |
|||
margin: 5px 0; |
|||
} |
|||
|
|||
header .home-menu, |
|||
header .home-menu .pure-menu-list, |
|||
header .home-menu .pure-menu-list li { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
line-height: normal !important; |
|||
flex-wrap:wrap; |
|||
} |
|||
|
|||
header .home-menu a { |
|||
color: var(--gold); |
|||
font-weight:900; |
|||
font-size:1.2em; |
|||
outline: none; |
|||
transition:background-color 0.33s ease-out, color 0.33s ease-out; |
|||
} |
|||
|
|||
header .home-menu a:hover, |
|||
header .home-menu .pure-menu-selected a { |
|||
color: #FFF!important; |
|||
background-color: var(--gold); |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
header .home-menu li a i { |
|||
margin-right:5px; |
|||
} |
|||
|
|||
.pure-menu.pure-menu-open, |
|||
.pure-menu.pure-menu-horizontal li .pure-menu-children { |
|||
border:none; |
|||
} |
|||
|
|||
.pure-menu .pure-menu-heading { |
|||
color: #FFF; |
|||
font-size: 1.2em; |
|||
border-bottom: 1px solid var(--grey); |
|||
margin:0; |
|||
padding: 16px 16px 12px 15px; |
|||
} |
|||
|
|||
.pure-menu li a:hover { |
|||
background-color:var(--dark-blue); |
|||
color:#FFF; |
|||
} |
|||
|
|||
/* PURE TABLE */ |
|||
.pure-table { |
|||
border: none; |
|||
} |
|||
|
|||
.pure-table thead { |
|||
background-color:var(--gold); |
|||
} |
|||
|
|||
.pure-table td { |
|||
vertical-align:top; |
|||
} |
|||
|
|||
.pure-table-odd td, |
|||
.pure-table-even td { |
|||
background-color: transparent; |
|||
} |
|||
|
|||
.responsiveTableLabel { |
|||
display:none; |
|||
visibility:hidden; |
|||
} |
|||
|
|||
/* HOME */ |
|||
.boxWelcome{ |
|||
background-color: var(--dark-blue); |
|||
color: white; |
|||
max-width:640px; |
|||
margin:15px auto 10px auto; |
|||
padding:5px 10px 10px; |
|||
box-shadow: -1px -1px 0 #000; |
|||
} |
|||
|
|||
/* Stats Boxes */ |
|||
.boxStats .l-box { |
|||
background-color: var(--dark-blue); |
|||
margin:10px; |
|||
padding:10px; |
|||
box-shadow: -1px -1px 0 #000; |
|||
} |
|||
|
|||
.boxStats h3 { |
|||
margin:0; |
|||
color: var(--gold); |
|||
text-shadow: -2px -2px 0px #000; |
|||
} |
|||
|
|||
.boxStatsList { |
|||
text-align:center; |
|||
} |
|||
|
|||
.boxStatsList ul { |
|||
display:inline-block; |
|||
margin: 0 auto; |
|||
padding:0; |
|||
text-align: left; |
|||
} |
|||
|
|||
.boxStatsList li { |
|||
list-style-type: none; |
|||
margin: 5px 0; |
|||
} |
|||
|
|||
/* Stats Page */ |
|||
.blocksFoundList { |
|||
clear:both; |
|||
display:flex; |
|||
flex-wrap:wrap; |
|||
justify-content: space-around; |
|||
} |
|||
|
|||
.blocksFoundList > div { |
|||
margin: 5px; |
|||
padding: 5px; |
|||
text-overflow: ellipsis; |
|||
box-shadow: -1px -1px 0 #000; |
|||
flex: 1; |
|||
min-width: 150px; |
|||
max-width: 150px; |
|||
} |
|||
|
|||
.blocksFoundPending { |
|||
color: #FFF; |
|||
background-color: var(--grey); |
|||
} |
|||
|
|||
.blocksFoundList .countLabel { |
|||
float:right; |
|||
font-size:0.8em; |
|||
line-height:1em; |
|||
} |
|||
|
|||
.blocksFoundPending .countLabel { |
|||
background-color: var(--blood); |
|||
border-radius:1px; |
|||
padding:1px; |
|||
} |
|||
|
|||
.blocksFoundPaid { |
|||
background-color: var(--dark-blue); |
|||
} |
|||
|
|||
.blocksFoundPaid .countLabel { |
|||
color: var(--gold); |
|||
font-weight:900; |
|||
} |
|||
|
|||
.blocksFoundHeader { |
|||
font-size: 1.3em; |
|||
margin: 10px 10px 20px 10px; |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
align-items: center; |
|||
} |
|||
|
|||
.blocksFoundHeader > div { |
|||
flex: 1; |
|||
} |
|||
|
|||
.blocksFoundHeader > div:nth-child(2) { |
|||
font-size:0.8em; |
|||
text-align:right; |
|||
} |
|||
|
|||
.minerAddress { |
|||
font-family: monospace; |
|||
} |
|||
|
|||
/* GETTING STARTED */ |
|||
.gettingStartedMenu, |
|||
.gettingStartedMenu.pure-menu-open { |
|||
width:100%; |
|||
display: inline-block; |
|||
background-color: var(--gold); |
|||
background: linear-gradient(to right, var(--gold) 0%,var(--dark-grey) 100%); |
|||
} |
|||
|
|||
.gettingStarted .menuHeader { |
|||
color: #FFF; |
|||
font-weight:900; |
|||
border-bottom: 1px solid var(--grey); |
|||
font-size: 1.2em; |
|||
padding: 16px 16px 12px 15px; |
|||
margin:0; |
|||
text-transform:uppercase; |
|||
} |
|||
|
|||
.gettingStartedMenu ul { |
|||
position: -webkit-sticky; |
|||
position:sticky; |
|||
top: 10px; |
|||
} |
|||
|
|||
.gettingStartedContent { |
|||
margin: 15px; |
|||
} |
|||
|
|||
.coinInfoHeader { |
|||
color: var(--gold); |
|||
margin:0; |
|||
text-shadow: -2px -2px 0px #000; |
|||
} |
|||
|
|||
/* PAYMENTS */ |
|||
.paymentblocks { |
|||
max-width:500px; |
|||
word-wrap:break-word; |
|||
position:relative; |
|||
} |
|||
|
|||
.paymentblocks .fade { |
|||
display:none; |
|||
} |
|||
|
|||
.paymentblocksexpand .fade { |
|||
display:block; |
|||
z-index: 10; |
|||
background-image: linear-gradient(to bottom, rgba(20, 20, 20, 0), rgba(20, 20, 20, 1) 90%); |
|||
width: 95%; |
|||
height: 10px; |
|||
position: absolute; |
|||
bottom: 7px; |
|||
pointer-events: none; |
|||
text-align: center; |
|||
font-size: 14px; |
|||
color: var(--gold); |
|||
transition: opacity 0.2s ease 0.33s; |
|||
} |
|||
|
|||
.paymentblocks a { |
|||
display: block; |
|||
max-height: 36px; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
transition: max-height 0.2s ease-out 0.33s; |
|||
} |
|||
|
|||
.paymentblocksexpand a:hover { |
|||
max-height: 1000px; |
|||
} |
|||
|
|||
.paymentblocksexpand a:hover + .fade { |
|||
opacity:0; |
|||
} |
|||
|
|||
/* WORKER STATS */ |
|||
.workerStatsChartHeaderLeft { |
|||
float:left; |
|||
max-width:100%; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.workerStatsChartHeaderRight { |
|||
float:right; |
|||
padding-left: 18px; |
|||
font-size: .8em; |
|||
} |
|||
|
|||
.workerStatsChartFooter { |
|||
display:flex; |
|||
} |
|||
|
|||
.workerStatsChartFooter > div { |
|||
flex:1; |
|||
text-align:center; |
|||
} |
|||
|
|||
.poolLabel { |
|||
font-size: 1.2em; |
|||
line-height: 1.5em; |
|||
text-align: center; |
|||
padding: 5px; |
|||
} |
|||
|
|||
/* CHARTS */ |
|||
#topCharts > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
|
|||
.chartWrapper{ |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
|
|||
.chartLabel { |
|||
font-size: 1.2em; |
|||
line-height: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
margin:0; |
|||
} |
|||
|
|||
.nvd3 .nv-axis path, |
|||
.nvd3 .nv-axis line { |
|||
fill: none; |
|||
stroke: #333333; |
|||
shape-rendering: crispEdges; |
|||
} |
|||
|
|||
.nv-axis text, |
|||
.nv-legend text { |
|||
font-size: 11px; |
|||
fill: #FFFFFF; |
|||
stroke: none; |
|||
} |
|||
|
|||
.nv-group.nv-series-0, |
|||
.nv-series:first-of-type .nv-legend-symbol { |
|||
stroke-opacity: 1; |
|||
fill-opacity: 0.5; |
|||
fill: var(--gold)!important; |
|||
stroke: var(--gold)!important; |
|||
} |
|||
|
|||
.nvtooltip { |
|||
color:#000; |
|||
} |
|||
|
|||
.nvtooltip .legend-color-guide div:first-of-type { |
|||
background-color: var(--gold)!important; |
|||
} |
|||
|
|||
@media only screen and (max-width: 760px), (min-device-width: 768px) and (max-device-width: 1024px) { |
|||
.home-menu li { |
|||
margin: 0 5px; |
|||
} |
|||
|
|||
.home-menu li a { |
|||
padding: 5px; |
|||
font-size: 1em; |
|||
line-height: 1em; |
|||
} |
|||
|
|||
.home-menu li a i { |
|||
margin-right:0; |
|||
} |
|||
|
|||
.pure-table { |
|||
border-top: 1px solid var(--grey); |
|||
} |
|||
|
|||
table, thead, tbody, th, td, tr { |
|||
display: block; |
|||
} |
|||
|
|||
thead tr { |
|||
display:none; |
|||
visiblity:hidden; |
|||
} |
|||
|
|||
tr { |
|||
border-bottom: 1px solid var(--grey); |
|||
} |
|||
|
|||
td { |
|||
border: none; |
|||
position: relative; |
|||
|
|||
} |
|||
|
|||
.pure-table td { |
|||
border-left:none; |
|||
padding: 5px; |
|||
max-width:100%; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
br.responsiveonly { |
|||
display:initial; |
|||
} |
|||
|
|||
.responsivehide { |
|||
display:none; |
|||
} |
|||
|
|||
.boxWelcome h1, |
|||
.boxWelcome h2 { |
|||
text-align:center!important; |
|||
} |
|||
|
|||
.blocksFoundHeader { |
|||
display:block; |
|||
} |
|||
|
|||
.blocksFoundHeader > div { |
|||
text-align:center!important; |
|||
} |
|||
|
|||
.blocksFoundList > div { |
|||
flex: 1; |
|||
} |
|||
|
|||
.responsiveTableLabel { |
|||
display:inline-block; |
|||
visibility:visible; |
|||
} |
|||
|
|||
.pure-responsive-disable, |
|||
.flex-responsive-disable { |
|||
width: 100%; |
|||
} |
|||
|
|||
.paymentblocks a { |
|||
display:inline; |
|||
} |
|||
|
|||
.paymentblocks .fade { |
|||
display:none; |
|||
} |
|||
} |
@ -0,0 +1,79 @@ |
|||
$(function() { |
|||
initStatData(); |
|||
|
|||
$('.btn-lg').click(function(){ |
|||
window.location = "workers/" + $('.input-lg').val(); |
|||
}); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
if (document.querySelector('#pageWorkers') !== null) { |
|||
var stats = JSON.parse(e.data); |
|||
|
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
var sharesTotal = 0; |
|||
var minerIndex = 0; |
|||
|
|||
for (var addr in stats.pools[pool].miners) { |
|||
minerIndex++; |
|||
var workerstat = stats.pools[pool].miners[addr]; |
|||
sharesTotal += parseFloat(workerstat.shares); |
|||
var existingRow = document.querySelector('#workers' + pool + ' #miner-' + minerIndex); |
|||
var minerEfficiency = ( workerstat.shares > 0 ) ? Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100 : 0; |
|||
|
|||
if (existingRow == null) { |
|||
//Add new
|
|||
var insertMinerTr = document.createElement('tr'); |
|||
insertMinerTr.id = 'miner-' + minerIndex; |
|||
insertMinerTr.setAttribute('data-hashrate', workerstat.hashrate); |
|||
insertMinerTr.innerHTML = '<td><span class="responsiveTableLabel"><i class="far fa-address-card fa-fw"></i></span>Miner #'+ minerIndex +'</td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span><span>' + bigNumber(workerstat.shares) + '</span></td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-bullseye fa-fw"></i> Efficiency: </span><span> ' + minerEfficiency + '%</span></td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-tachometer-alt fa-fw"></i> Hashrate: </span><span>' + workerstat.hashrateString + '</span></td>'; |
|||
|
|||
document.querySelector('#workers' + pool + ' .poolMinerTable tbody').appendChild(insertMinerTr); |
|||
|
|||
console.log('Added new miner! [' + minerIndex + ']'); |
|||
} else { |
|||
//Update existing
|
|||
document.querySelector('#workers' + pool + ' #miner-' + minerIndex + ' td:nth-child(2) span:nth-child(2)').innerHTML = bigNumber(workerstat.shares); |
|||
document.querySelector('#workers' + pool + ' #miner-' + minerIndex + ' td:nth-child(3) span:nth-child(2)').innerHTML = minerEfficiency + '%'; |
|||
document.querySelector('#workers' + pool + ' #miner-' + minerIndex + ' td:nth-child(4) span:nth-child(2)').innerHTML = workerstat.hashrateString; |
|||
document.querySelector('#workers' + pool + ' #miner-' + minerIndex).setAttribute('data-hashrate', workerstat.hashrate); |
|||
} |
|||
} |
|||
|
|||
document.querySelector('#statsShares' + pool).innerHTML = bigNumber(sharesTotal); |
|||
|
|||
//Remove inactive
|
|||
var workerList = document.querySelectorAll('#workers' + pool + ' .poolMinerTable tbody tr'); |
|||
for ( var i = workerList.length-1; i >= 0; i--) { |
|||
if( typeof stats.pools[pool].miners[workerList[i].id] == 'undefined') { |
|||
console.log('Removing miner :( [' + workerTrID + ']'); |
|||
workerList[i].parentNode.removeChild(workerList[i]); |
|||
} |
|||
} |
|||
|
|||
//Resort table
|
|||
var table = document.querySelector('#workers' + pool + ' table.poolMinerTable tbody'); |
|||
var rows = document.querySelectorAll('#workers' + pool + ' table.poolMinerTable tbody tr'); |
|||
var rowsArr = [].slice.call(rows).sort(function (a, b) { |
|||
return (parseFloat(a.dataset.hashrate) == parseFloat(b.dataset.hashrate)) ? 0 : ((parseFloat(a.dataset.hashrate) < parseFloat(b.dataset.hashrate)) ? 1 : -1); |
|||
}); |
|||
for (var i = 0; i < rowsArr.length; i++){ table.append(rowsArr[i]); } |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function searchKeyPress(e) |
|||
{ |
|||
// look for window.event in case event isn't passed in
|
|||
e = e || window.event; |
|||
if (e.keyCode == 13) |
|||
{ |
|||
document.getElementById('btnSearch').click(); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
@ -0,0 +1,92 @@ |
|||
<!doctype html> |
|||
|
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
|
|||
<link rel="shortcut icon" type="image/png" href="/static/skull.png"/> |
|||
|
|||
<!--<meta name="viewport" content="width=device-width, initial-scale=1">--> |
|||
|
|||
<link href='//fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'> |
|||
|
|||
<!-- <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css"> --> |
|||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous"> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css"> |
|||
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/pure/0.4.2/pure-min.css"> |
|||
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script> |
|||
|
|||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.5/d3.min.js"></script> |
|||
<script src="/static/nvd3.js"></script> |
|||
<link rel="stylesheet" href="/static/nvd3.css"> |
|||
|
|||
<script src="/static/main.js"></script> |
|||
<link rel="stylesheet" href="/static/style.css"> |
|||
|
|||
<title>zz nomp - mining pool for pirate chain</title> |
|||
|
|||
<meta name="google-site-verification" content="JxkbxOT-aLGRVzTv8ES0RQ7EaWdSGZvOfZOUVmF1Xm8" /> |
|||
</head> |
|||
|
|||
<body> |
|||
|
|||
<header> |
|||
<div style="text-align: center;"><span style="font-size:1.5em; color:red; font-weight:900;text-shadow: 1px 1px 1px #fff;">SAPLING on piratepool.io enabled! Immediate action required! See notice on <a href="/#sapling">homepage</a>!</span></div> |
|||
<div class="home-menu pure-menu pure-menu-open pure-menu-horizontal"> |
|||
|
|||
<a class="pure-menu-heading {{? it.selected.length < 1 }}pure-menu-selected{{?}} hot-swapper" href="/"><i class="fa fa-home"></i> Home</a> |
|||
|
|||
<ul> |
|||
<li class="{{? it.selected === 'getting_started' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/getting_started"> |
|||
<i class="fas fa-rocket fa-fw"></i> |
|||
Getting Started |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'stats' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/stats"> |
|||
<i class="fas fa-chart-bar fa-fw"></i> |
|||
Graph Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'tbs' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/tbs"> |
|||
<i class="fas fa-table fa-fw"></i> |
|||
Tab Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'workers' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/workers"> |
|||
<i class="fas fa-cogs fa-fw"></i> |
|||
Workers Stats |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'payments' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/payments"> |
|||
<i class="fab fa-btc fa-fw"></i> |
|||
Payments |
|||
</a> |
|||
</li> |
|||
<li class="{{? it.selected === 'api' }}pure-menu-selected{{?}}"> |
|||
<a class="hot-swapper" href="/api"> |
|||
<i class="fas fa-code fa-fw"></i> |
|||
API |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
</div> |
|||
</header> |
|||
|
|||
<main> |
|||
{{=it.page}} |
|||
</main> |
|||
|
|||
<footer> |
|||
No warranties matey! If ye be 'aving any troubles, invoke yer right to parlay at <a href="https://discord.gg/5f7p3vx" target="_blank" rel="noopener noreferrer">#pirate</a> |
|||
<br> |
|||
<3 <a href="https://webworker.sh" alt="webworker01" target="_blank" rel="noopener noreferrer">🐸</a> · Built with <a href="https://github.com/webworker01/knomp" target="_blank" rel="noopener noreferrer">knomp</a> |
|||
</footer> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,50 @@ |
|||
<div> |
|||
|
|||
<style> |
|||
#passwordForm, #adminCenter{ |
|||
display: none; |
|||
} |
|||
#adminCenter{ |
|||
display: flex; |
|||
flex-flow: row; |
|||
} |
|||
#leftMenu{ |
|||
flex: 0 0 200px; |
|||
} |
|||
#editForm{ |
|||
flex: 1 1 auto; |
|||
} |
|||
</style> |
|||
|
|||
<form class="pure-form pure-form-stacked" id="passwordForm"> |
|||
<fieldset> |
|||
<legend>Password</legend> |
|||
|
|||
<input id="password" type="password" placeholder="Password"> |
|||
|
|||
<label for="remember" class="pure-checkbox"> |
|||
<input id="remember" type="checkbox"> Stay Logged In |
|||
</label> |
|||
|
|||
<button type="submit" class="pure-button pure-button-primary">Log In</button> |
|||
</fieldset> |
|||
</form> |
|||
|
|||
<div id="adminCenter"> |
|||
|
|||
<div class="pure-menu pure-menu-open" id="leftMenu"> |
|||
<a class="pure-menu-heading">Administration</a> |
|||
<ul> |
|||
<li id="addPool"><a href="#">Add Pool</a></li> |
|||
<li class="pure-menu-heading" id="poolList">Current Pools</li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div id="editForm"></div> |
|||
|
|||
</div> |
|||
|
|||
|
|||
<script src="/static/admin.js"></script> |
|||
|
|||
</div> |
@ -0,0 +1,11 @@ |
|||
<div style="margin: 18px;"> |
|||
API - The API is work in progress and is subject to change during development. |
|||
<ul> |
|||
<li><a href="/api/stats">/api/stats</a> global pool stats</li> |
|||
<li><a href="/api/blocks">/api/blocks</a> global block stats</li> |
|||
<li><a href="/api/pool_stats">/api/pool_stats</a> - historical stats</li> |
|||
<li><a href="/api/payments">/api/payments</a> - payment history</li> |
|||
<li><a href="/api/worker_stats?addr">/api/worker_stats?<replace address here></a> - historical time per pool json </li> |
|||
<li><a href="/api/live_stats">/api/live_stats</a> - live stats (websocket)</li> |
|||
</ul> |
|||
</div> |
@ -0,0 +1,265 @@ |
|||
<div id="holder"> |
|||
<div class="pure-menu pure-menu-open gettingStartedMenu"> |
|||
<span class="pure-menu-heading">Getting Started</span> |
|||
|
|||
<ul class="pure-menu-list"> |
|||
<li class="pure-menu-item"><a href="#poolDetails" class="pure-menu-link">Pool Details</a></li> |
|||
<li class="pure-menu-item"><a href="#payments" class="pure-menu-link">Payout Information</a></li> |
|||
<li class="pure-menu-item"><a href="#createWallet" class="pure-menu-link">Create Wallet</a></li> |
|||
<li class="pure-menu-item"><a href="#gpuMining" class="pure-menu-link">GPU Mining Software</a></li> |
|||
<li class="pure-menu-item"><a href="#cpuMining" class="pure-menu-link">CPU Mining Software</a></li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div id="menu"> |
|||
{{? (function(){ |
|||
if (!it.portalConfig.switching) return false; |
|||
for (var p in it.portalConfig.switching){ |
|||
if (it.portalConfig.switching[p].enabled) |
|||
return true; |
|||
} |
|||
return false; |
|||
})() |
|||
}} |
|||
<div class="menuHeader">Coin-Switching Ports</div> |
|||
{{?}} |
|||
|
|||
<div class="menuList"> |
|||
{{ for (var p in it.portalConfig.switching){ |
|||
if (!it.portalConfig.switching[p].enabled) continue; |
|||
var info = { |
|||
algo: p, |
|||
ports: {}, |
|||
host: it.portalConfig.website.stratumHost |
|||
}; |
|||
info.ports[it.portalConfig.switching[p].port] = {diff: it.portalConfig.switching[p].diff}; |
|||
info = JSON.stringify(info).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=p}}</a> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
<div class="menuHeader" id="poolDetails">Miner Configuration</div> |
|||
<div class="menuList" id="coinList"> |
|||
{{ if (it.portalConfig.gettingStartedPopups) { }} |
|||
{{ for (var pool in it.poolsConfigs) { |
|||
var info = JSON.stringify({ |
|||
coin: it.poolsConfigs[pool].coin, |
|||
algo: it.poolsConfigs[pool].coin.algorithm, |
|||
ports: it.poolsConfigs[pool].ports, |
|||
host: it.portalConfig.website.stratumHost |
|||
}).replace(/"/g, '"'); |
|||
}} |
|||
<a href="#" class="poolOption" data-info="{{=info}}">{{=pool}}</a> |
|||
{{ } }} |
|||
{{ } else { }} |
|||
{{ for (var pool in it.poolsConfigs) { }} |
|||
{{if (String(pool) == 'pirate') { continue; } }} |
|||
<code> |
|||
<div class="coinInfoHeader"><span class="coinInfoName">{{=it.poolsConfigs[pool].coin.name}}</span> Configuration:</div> |
|||
<div class="coinInfoRows"> |
|||
<div class="coinInfoRowKeys"> |
|||
<div>Username:</div> |
|||
<div>Password:</div> |
|||
<div>Algorithm:</div> |
|||
{{ for (var port in it.poolsConfigs[pool].ports) { }} |
|||
<div class="coinInfoData">{{= it.poolsConfigs[pool].ports[port].label ? it.poolsConfigs[pool].ports[port].label : 'URL'}} <span class="coinInfoSubtle">(diff {{=it.poolsConfigs[pool].ports[port].diff}})</span></div> |
|||
{{ } }} |
|||
</div> |
|||
<div class="coinInfoRowValues"> |
|||
<div class="coinInfoUsername"> |
|||
{{ if (it.poolsConfigs[pool].coin && it.poolsConfigs[pool].coin.privateChain) { }} |
|||
your {{=it.poolsConfigs[pool].coin.name}} <b style="color:#C99631;">zs1</b> sapling address<br> |
|||
|
|||
{{ } else if (it.poolsConfigs[pool].coin) { }} |
|||
your {{=it.poolsConfigs[pool].coin.name}} wallet address |
|||
{{ } else { }} |
|||
your public key |
|||
{{ } }} |
|||
</div> |
|||
<div>anything</div> |
|||
<div>{{=it.poolsConfigs[pool].coin.algorithm}}</div> |
|||
{{ for (var port in it.poolsConfigs[pool].ports) { }} |
|||
<div class="coinInfoData">stratum+tcp://{{=it.portalConfig.website.stratumHost}}:{{=port}}</div> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
</code> |
|||
{{ } }} |
|||
|
|||
<div class="content"> |
|||
If you have multiple mining rigs you can add a label at the end of your username to see stats broken down by rig on the worker stats page. |
|||
</div> |
|||
<code style="word-break:break-all;"> |
|||
<b><em>DSTM</em></b><br> |
|||
zm --server {{=it.portalConfig.website.stratumHost}} --port {{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} --user zs1gfnu0j84arzuv4dauryyvys07m0p5vwd8grl4whz08y769jmzj7zar84hewngtgskce4vex4udc.<span style="text-decoration:underline; font-weight:900;">myrigname</span> --pass x -dev 0 1 --temp-target=0:70,1:70 --intensity=0:0.5,1:0.5 --color --time |
|||
</code> |
|||
<code style="word-break:break-all;"> |
|||
<b><em>EWBF</em></b><br> |
|||
miner --server {{=it.portalConfig.website.stratumHost}} --user zs1gfnu0j84arzuv4dauryyvys07m0p5vwd8grl4whz08y769jmzj7zar84hewngtgskce4vex4udc.<span style="text-decoration:underline; font-weight:900;">myrigname</span> --pass x --port {{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} --fee 0 --pec --templimit 70 --intensity 50 |
|||
</code> |
|||
<code style="word-break:break-all;"> |
|||
<b><em>BMINER</em></b><br> |
|||
bminer -uri stratum://zs1gfnu0j84arzuv4dauryyvys07m0p5vwd8grl4whz08y769jmzj7zar84hewngtgskce4vex4udc.<span style="text-decoration:underline; font-weight:900;">myrigname</span>@{{=it.portalConfig.website.stratumHost}}:{{= Object.keys(it.poolsConfigs[Object.keys(it.poolsConfigs)[0]].ports)[0] }} -max-temperature 70 -nofee |
|||
</code> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
<div class="menuHeader" id="payments">Payments</div> |
|||
<div class="content"> |
|||
On the sapling pool, payouts should take anywhere from 10 to 30 minutes.<br><br> |
|||
There is a limit of 200 receipients that can be included in a payout, hence the high min payout set on the pool. If payments stop going out, do not worry! Funds are safu! Blocks can still be found and will be credited to your username!<br><br> |
|||
Most likely the min payout of the pool needs to be adjusted upwards. Please check the <a href="https://discord.gg/5f7p3vx">#pools-and-operators channel in discord</a> to see if it's already being worked on. <br><br> |
|||
The longer it takes to make a payout, the more people meet the min payout making the following payout take longer. If you scroll down the payments page compare the number of miners paid versus the time between payments and you will see the correlation.<br><br> |
|||
Pending is supposed to be time to maturity before a block gets paid which I have set to 10 to try to coincide with dPoW, but I have doubts this is working right and believe it's just a visual change (need to verify this in the code)<br><br> |
|||
Immature and balance on your worker stats page are the unpaid balances accrued from previous payout rounds, it does not count blocks that have not yet been attempted to be paid by the pool<br><br> |
|||
Payments that show up on the payments page initially are in the process of constructing the z transaction, once it actually broadcasts the transaction it will switch to being hyperlinked and then should arrive in miners' wallets in the next block or two. |
|||
</div> |
|||
|
|||
<div class="menuHeader" id="createWallet">Generate Wallet and Address</div> |
|||
<div class="content"> |
|||
<a href="https://pirate.black/wallets/" target="_blank" rel="noopener noreferrer">GUI Wallet for PIRATE</a> is available. For Agama support please visit <a href="https://discord.gg/5f7p3vx">#newpirates and #agama-wallet on Discord</a><br><br> |
|||
|
|||
CLI Wallet: |
|||
<ol> |
|||
<li><a href="https://docs.komodoplatform.com/komodo/install-Komodo-manually.html" target="_blank">Build Komodo</a></li> |
|||
<li>Launch PIRATE chain: |
|||
<code> |
|||
./komodod -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=136.243.102.225 |
|||
</code> |
|||
</li> |
|||
<li>Generate Z address: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_getnewaddress |
|||
</code> |
|||
</li> |
|||
<li>Securely backup your private key: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_exportkey "zaddr" |
|||
</code> |
|||
</li> |
|||
<li>Check your balance: |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE z_gettotalbalance |
|||
</code> |
|||
<li> |
|||
For more information visit the <a href="https://pirate.black/" target="_blank">project website</a> |
|||
</li> |
|||
</ol> |
|||
</div> |
|||
|
|||
<div class="menuHeader" id="gpuMining">GPU Mining</div> |
|||
<div class="content"> |
|||
<p> |
|||
There are a few decent GPU miners available. You will need to experiment to find which one works best for you. DYOR on this and be sure to virus scan all the things! |
|||
</p> |
|||
<p> |
|||
Here are a couple well-known GPU miners: |
|||
</p> |
|||
<ul> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=2021765.0" target="_blank" rel="noopener noreferrer">dstm's ZCash / Equihash Nvidia Miner</a></li> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=1707546.0" target="_blank" rel="noopener noreferrer">EWBF's CUDA Zcash miner 0.3.4b</a></li> |
|||
<li><a href="https://www.bminer.me/releases/" target="_blank" rel="noopener noreferrer">Bminer Lite</a></li> |
|||
<li><a href="https://bitcointalk.org/index.php?topic=1670733.0" target="_blank" rel="noopener noreferrer">Claymore's ZCash/BTG AMD GPU Miner</a></li> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div class="menuHeader" id="cpuMining">CPU Mining</div> |
|||
<div class="content"> |
|||
<p>While it is possible to CPU mine with <b>komodod</b> directly like so:</p> |
|||
<code> |
|||
./komodo-cli -ac_name=PIRATE setgenerate true |
|||
</code> |
|||
<p>The chances to find a block solo with CPU mining only are already fairly low and most likely the reason you are here at all 🐸</p> |
|||
<p>The well-known CPU miner that can be used with the pool is <b>nheqminer</b>. This does <i>not</i> require a Nicehash account and can be used to connect to any equihash stratum.</p> |
|||
|
|||
<h4>Linux</h4> |
|||
<p>It is suggested that you attempt to build the CPU miner instead of using the pre-compiled binary so that optimizations for your CPU can be made during the build process</p> |
|||
<code> |
|||
sudo apt-get install cmake build-essential libboost-all-dev<br> |
|||
cd ~<br> |
|||
git clone -b Linux https://github.com/nicehash/nheqminer.git<br> |
|||
cd ~/nheqminer/cpu_xenoncat/Linux/asm/<br> |
|||
sh assemble.sh<br> |
|||
cd /home/zcash/nheqminer/Linux_cmake/nheqminer_cpu<br> |
|||
cmake . && make<br> |
|||
ln -s /home/zcash/nheqminer/Linux_cmake/nheqminer_cpu/nheqminer_cpu /usr/local/bin/nheqminer_cpu |
|||
</code> |
|||
<p>Now you can launch your miner! (replace the values as indicated changing the number of threads (-t) and your address for payouts (-u)</p> |
|||
<code> |
|||
nheqminer_cpu -t <i>numberofthreads</i> -l <i>stratumhost</i>:<i>stratumport</i> -u <i>youraddress</i> |
|||
</code> |
|||
|
|||
<h4>Windows</h4> |
|||
<p>Download and install the latest release for windows from <a href="https://github.com/nicehash/nheqminer/releases" target="_blank" rel="noopener noreferrer">the github repo</a></p> |
|||
<p>Replace the placeholders below as needed and start mining!</p> |
|||
<code> |
|||
nheqminer -t <i>numberofthreads</i> -l <i>stratumhost</i>:<i>stratumport</i> -u <i>youraddress</i> |
|||
</code> |
|||
|
|||
<h4>Additional Resources</h4> |
|||
<ul> |
|||
<li><a href="https://github.com/nicehash/nheqminer" target="_blank" rel="noopener noreferrer">nheqminer repo</a></li> |
|||
<li><a href="https://steemit.com/mining/@bobinson/zcash-cpu-mining-setup-on-linux" target="_blank" rel="noopener noreferrer">CPU Mining</a></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{{ if (it.portalConfig.gettingStartedPopups) { }} |
|||
<a href="#" id="coinInfoBackground" class="hidden"></a> |
|||
|
|||
<div id="coinInfo" class="hidden"> |
|||
<a href="#" id="coinInfoClose">×</a> |
|||
<div><span class="coinInfoName"></span> Configuration:</div> |
|||
<div id="coinInfoRows"> |
|||
<div id="coinInfoRowKeys"> |
|||
<div>Username:</div> |
|||
<div>Password:</div> |
|||
</div> |
|||
<div id="coinInfoRowValues"> |
|||
<div id="coinInfoUsername"></div> |
|||
<div>anything</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script> |
|||
function showCoinConfig(info){ |
|||
|
|||
var htmlKeys = '<div class="coinInfoData">Algorithm:</div>'; |
|||
var htmlValues = '<div class="coinInfoData">' + info.algo + '</div>'; |
|||
|
|||
for (var port in info.ports){ |
|||
htmlKeys += '<div class="coinInfoData">URL <span class="coinInfoSubtle">(difficulty ' + info.ports[port].diff + ')</span>:</div>'; |
|||
htmlValues += '<div class="coinInfoData">stratum+tcp://' + info.host + ':' + port + '</div>'; |
|||
} |
|||
|
|||
if (info.coin && info.coin.privateChain) { |
|||
$('#coinInfoUsername').html('your ' + info.coin.name + ' <b style="color:#C99631;">Z</b> wallet address'); |
|||
} else if (info.coin) { |
|||
$('#coinInfoUsername').text('your ' + info.coin.name + ' wallet address'); |
|||
} else { |
|||
$('#coinInfoUsername').text('your public key'); |
|||
} |
|||
$('.coinInfoData').remove(); |
|||
$('#coinInfoRowKeys').append(htmlKeys); |
|||
$('#coinInfoRowValues').append(htmlValues); |
|||
} |
|||
|
|||
$('.poolOption').click(function(event){ |
|||
event.preventDefault(); |
|||
showCoinConfig($(this).data('info')); |
|||
$('#coinInfoBackground,#coinInfo').removeClass('hidden'); |
|||
$('#coinInfoBackground').css('opacity', 0.7); |
|||
return false; |
|||
}); |
|||
|
|||
$('#coinInfoBackground,#coinInfoClose').click(function(event){ |
|||
event.preventDefault(); |
|||
$('#coinInfoBackground,#coinInfo').addClass('hidden'); |
|||
$('#coinInfoBackground').css('opacity', 0.0); |
|||
return false; |
|||
}); |
|||
</script> |
|||
{{ } }} |
@ -0,0 +1,147 @@ |
|||
<div class="pure-g-r" id="boxWelcome"> |
|||
<div class="pure-u-1" style="text-align:center; padding:10px 0;"> |
|||
<div class="pure-u-1-2"> |
|||
<img src="/static/skull256.png" style="float:left; width:128px;" alt="zz nomp"> |
|||
<div> |
|||
<div style="font-size: 2em; text-align:center; margin-bottom:10px; font-weight:900; color:#1F152A;">zz nomp</div> |
|||
|
|||
<div style="text-align:center;" class="pure-g-r"> |
|||
<div class="pure-u-1-2" style="text-align:right;"> |
|||
For Pirate Chain » |
|||
</div> |
|||
<div class="pure-u-1-2" style="text-align:left;"> |
|||
<div class="pure-g-r"> |
|||
<div class="pure-u-1-2" style="text-align:left;"> |
|||
<a href="https://pirate.black/" target="_blank" rel="noopener noreferrer"><i class="fa fa-globe fa-fw"></i> Website</a><br> |
|||
<a href="https://github.com/PirateNetwork" target="_blank" rel="noopener noreferrer"><i class="fab fa-github fa-fw"></i> Github</a><br> |
|||
<a href="https://bitcointalk.org/index.php?topic=4979549" target="_blank" rel="noopener noreferrer"><i class="fab fa-bitcoin fa-fw"></i> BTT</a> |
|||
</div> |
|||
<div class="pure-u-1-2" style="text-align:left;"> |
|||
<a href="http://pirate.explorer.dexstats.info/" target="_blank" rel="noopener noreferrer"><i class="fas fa-map fa-fw"></i> Explorer</a><br> |
|||
<a href="https://discord.gg/5f7p3vx" target="_blank" rel="noopener noreferrer"><i class="fab fa-discord fa-fw"></i> Discord</a><br> |
|||
<a href="https://twitter.com/PirateChain" target="_blank" rel="noopener noreferrer"><i class="fab fa-twitter fa-fw"></i> Twitter</a> |
|||
</div> |
|||
<div class="pure-u-1"> |
|||
<a href="https://digitalprice.io/?inviter=4fdaf7" target="_blank" rel="noopener noreferrer"><i class="fas fa-chart-line fa-fw"></i> Trading at DigitalPrice.io</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="pure-u-1" style="text-align:center; padding:10px 0;"> |
|||
<table style="width: auto;margin: 0 auto;text-align: left;"> |
|||
<tr><td>Payout Frequency:</td><td>~20 Minutes</td></tr> |
|||
<tr><td>Min Payout:</td><td>10 ARRR (256 for SPROUT pool)</td></tr> |
|||
<tr><td>Pool Fee:</td><td>1%</td></tr> |
|||
<tr><td colspan="2"> |
|||
<br> |
|||
<em>1/3rd of the pool fee is donated to the PIRATE <a href="https://dexstats.info/onboarding.php" target="_blank" rel="noopener noreferrer">onboarding</a> <a href="http://pirate.explorer.dexstats.info/address/RAzq6y7dsUKgfuzNjpzyGiuFzvrwuDheQw" target="_blank" rel="noopener noreferrer">fund</a> |
|||
<br> and 1/3rd is donated to <a href="http://pirate.explorer.dexstats.info/address/RD5PhyAUhapsvj5ps2cCHozsXZfQSvDdrZ" target="_blank" rel="noopener noreferrer">PIRATE Marketing</a>!</em> |
|||
<br><br> |
|||
<a name="sapling"></a> |
|||
<span style="font-size:1.5em; color:#800000; font-weight:900;text-shadow: 4px 4px 2px #624c4c;">SAPLING Pool has been activated!</span> |
|||
<br><br> |
|||
Please create a sapling address (<span style="color:#800000; font-weight:900;text-shadow: 4px 4px 2px #624c4c;">starts with zs1</span>) and connect to the<br> |
|||
stratum ports labeled as <strong><em>arrr</em></strong> on <a href="/getting_started" class="hot-swapper">Getting Started</a>. |
|||
<br><br> |
|||
Target for shut down of sprout stratum is <span style="color:#800000; font-weight:900;text-shadow: 4px 4px 2px #624c4c;">2018-12-20!</span> |
|||
<br><br> |
|||
<span style="color:#800000; font-weight:900;text-shadow: 4px 4px 2px #624c4c;">Why should I switch?</span><br> |
|||
Min payout has been significantly reduced for the sapling pool only<br> |
|||
(original pool still at 256) and may be reduced further in the future,<br> |
|||
but we need more data to determine the proper min payout. |
|||
<br><br> |
|||
Also sapling payments significantly reduce the load on the server, so<br> |
|||
the stratum and website will respond faster! |
|||
<br><br> |
|||
There is only a 2 month window for the transition to sapling, so we<br> |
|||
need all the funds moved to your own wallets so you can migrate them<br> |
|||
to sapling with plenty of time. |
|||
<br><br> |
|||
So please switch as soon as possible to get your ARRR delivered faster! |
|||
<br><br> |
|||
<span style="color:#800000; font-weight:900;text-shadow: 4px 4px 2px #624c4c;">What about balances in my old address?</span><br> |
|||
Once all (most?) miners have switched over, final payout of the sprout<br> |
|||
pool will be made with no minimum balance. |
|||
<br><br> |
|||
Alternative pools: |
|||
<a href="https://piratechain.org/" target="_blank" rel="noopener noreferrer">PirateChain.org</a> |
|||
<a href="https://polly.chainstrike.io" target="_blank" rel="noopener noreferrer">Polly</a> |
|||
<a href="http://minethepiratechain.black" target="_blank" rel="noopener noreferrer">MTPC</a> |
|||
<a href="http://pirate.bcmonster.com/" target="_blank" rel="noopener noreferrer">BCMonster</a> |
|||
<a href="http://pirate.coolmine.top/" target="_blank" rel="noopener noreferrer">CoolMine</a> |
|||
</td></tr> |
|||
</table> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-g-r" id="boxesLower"> |
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsLeft"> |
|||
<div class="boxLowerHeader">Network Stats</div> |
|||
<div class="boxStatsList"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div> |
|||
<div><i class="fas fa-coins fa-fw"></i> {{=pool}}</div> |
|||
<div><i class="fas fa-link fa-fw" aria-hidden="true"></i> Block Height: <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> Network Hash/s: <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></div> |
|||
<div><i class="fas fa-unlock-alt fa-fw" aria-hidden="true"></i> Difficulty: <span id="statsNetworkDiff{{=pool}}">{{=it.stats.pools[pool].poolStats.networkDiff}}</span></div> |
|||
<div><i class="fas fa-users fa-fw"></i> Node Connections: <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></div> |
|||
</div> |
|||
{{ } }} |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">Pools / Coins</div> |
|||
<div class="boxStatsList"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div> |
|||
<div><i class="fas fa-coins fa-fw"></i> {{=pool}}</div> |
|||
<div><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</div> |
|||
<div><i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</div> |
|||
<div><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script type="text/javascript" src="/static/home.js" /> |
|||
<script> |
|||
//document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js'; |
|||
|
|||
$(function() { |
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
var stats = JSON.parse(e.data); |
|||
for (var pool in stats.pools) { |
|||
$('#statsMiners' + pool).text(stats.pools[pool].minerCount); |
|||
$('#statsWorkers' + pool).text(stats.pools[pool].workerCount); |
|||
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays); |
|||
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks); |
|||
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)).toFixed(8)); |
|||
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks); |
|||
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff); |
|||
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols)); |
|||
$('#statsNetworkConnections' + pool).text(stats.pools[pool].poolStats.networkConnections); |
|||
} |
|||
|
|||
for (algo in stats.algos) { |
|||
$('#statsMiners' + algo).text(stats.algos[algo].workers); |
|||
$('#statsHashrate' + algo).text(stats.algos[algo].hashrateString); |
|||
} |
|||
}); |
|||
}); |
|||
</script> |
@ -0,0 +1,28 @@ |
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<div class="chartLabel"> |
|||
<!--<div style="float:left; padding-right: 18px;"><i class="fa fa-users"></i><span id="statsWorkers">...</span></div>--> |
|||
<div style="float:left; margin-right: 9px;">{{=String(it.stats.address).split(".")[0].length > 40 ? String(it.stats.address).split(".")[0].substring(0, 20) + '...' + String(it.stats.address).split(".")[0].substring(String(it.stats.address).split(".")[0].length-20, String(it.stats.address).split(".")[0].length): String(it.stats.address).split(".")[0]}}</div> |
|||
|
|||
<div style="float:right; padding-left: 18px;"><small><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg">...</span> (Avg)</small></div> |
|||
<div style="float:right; padding-left: 18px;"><small><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate">...</span> (Now)</small></div> |
|||
<div style="float:right; padding-left: 18px;"><small><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays">...</span> Days</small></div> |
|||
</div> |
|||
<div class="chartHolder"><svg id="workerHashrate"></svg></div> |
|||
<div> |
|||
<div style="float:right; padding-top: 9px; padding-right: 18px;"><i class="fas fa-cog fa-fw"></i> Shares: <span id="statsTotalShares">...</span></div> |
|||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fas fa-money-bill-wave fa-fw"></i> Immature: <span id="statsTotalImmature">...</span> </div> |
|||
<div style="float:left; padding-top: 9px; padding-left: 18px; padding-right: 18px;"><i class="fas fa-money-bill-wave fa-fw"></i> Bal: <span id="statsTotalBal">...</span> </div> |
|||
<div style="padding-top: 9px; padding-left: 18px;"><i class="fas fa-money-bill-wave fa-fw"></i> Paid: <span id="statsTotalPaid">...</span> </div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="boxesWorkers"> </div> |
|||
|
|||
<script> |
|||
var _miner = "{{=String(it.stats.address).split(".")[0]}}"; |
|||
var _workerCount = 0; |
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/miner_stats.js'; |
|||
</script> |
@ -0,0 +1,25 @@ |
|||
<style> |
|||
#miningKeyPage{ |
|||
margin: 15px; |
|||
} |
|||
#keyFrame{ |
|||
padding: 0; |
|||
border: 0; |
|||
width: 100%; |
|||
height: 750px; |
|||
display: block; |
|||
} |
|||
</style> |
|||
|
|||
<div id="miningKeyPage"> |
|||
|
|||
<p> |
|||
This script run client-side (in your browser). For maximum security <a href="/key.html" download="key.html">download</a> the script and run it locally and |
|||
offline in a modern web browser. |
|||
</p> |
|||
|
|||
<iframe id="keyFrame" src="/key.html"> |
|||
|
|||
</iframe> |
|||
|
|||
</div> |
@ -0,0 +1,61 @@ |
|||
<script type="text/javascript"> |
|||
$(function () { |
|||
$(document).tooltip({ |
|||
content: function () { |
|||
return $(this).prop('title'); |
|||
}, |
|||
show: null, |
|||
close: function (event, ui) { |
|||
ui.tooltip.hover( |
|||
|
|||
function () { |
|||
$(this).stop(true).fadeTo(400, 1); |
|||
}, |
|||
|
|||
function () { |
|||
$(this).fadeOut("400", function () { |
|||
$(this).remove(); |
|||
}) |
|||
}); |
|||
} |
|||
}); |
|||
}); |
|||
</script> |
|||
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }} |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Blocks</th> |
|||
<th>Time</th> |
|||
<th>Miners</th> |
|||
<th>Shares</th> |
|||
<th>Amount</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var p in it.stats.pools[pool].payments) { }} |
|||
<tr> |
|||
<td class="paymentblocks" title="{{=it.stats.pools[pool].payments[p].opid}}"> |
|||
{{if (String(it.stats.pools[pool].name).startsWith("zcash")) { }} |
|||
<a href="https://explorer.zcha.in/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name).startsWith("zclassic")) { }} |
|||
<a href="https://classic.zcha.in/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name).startsWith("hush")) { }} |
|||
<a href="https://explorer.myhush.org/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (String(it.stats.pools[pool].name).startsWith("zen")) { }} |
|||
<a href="http://node1.zenchain.info:8886/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else if (typeof it.stats.pools[pool].payments[p].txid !== 'undefined' && (String(it.stats.pools[pool].name).startsWith("pirate") || String(it.stats.pools[pool].name).startsWith("arrr")) ) { }} |
|||
<a href="https://explorer.pirate.black/tx/{{=it.stats.pools[pool].payments[p].txid}}" title="View transaction" target="_blank">{{=it.stats.pools[pool].payments[p].blocks}}</a> |
|||
{{ } else { }} |
|||
{{=it.stats.pools[pool].payments[p].blocks}} |
|||
{{ } }} |
|||
</td> |
|||
<td>{{=readableDate(it.stats.pools[pool].payments[p].time)}}</td> |
|||
<td>{{=it.stats.pools[pool].payments[p].miners}}</td> |
|||
<td>{{=Math.round(it.stats.pools[pool].payments[p].shares)}}</td> |
|||
<td>{{=it.stats.pools[pool].payments[p].paid}} {{=it.stats.pools[pool].symbol}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
|||
</div> |
|||
{{ } }} |
@ -0,0 +1,261 @@ |
|||
<div id="topCharts"> |
|||
<div class="chartWrapper"> |
|||
<div class="chartLabel">Pool Historical Hashrate</div> |
|||
<div class="chartHolder"><svg id="poolHashrate"></svg></div> |
|||
</div> |
|||
</div> |
|||
|
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ function readableDate(a){ return new Date(parseInt(a)).toISOString().substring(0, 16).replace('T', ' ') + ' UTC'; } }} |
|||
|
|||
<div class="pure-g-r" id="boxesLower"> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsLeft"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Pool Stats</div> |
|||
<div class="boxStatsList"> |
|||
<div> |
|||
<div><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners</div> |
|||
<div><i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers</div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate{{=pool}}">{{=it.stats.pools[pool].hashrateString}}</span> (Now)</div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg{{=pool}}">...</span> (Avg)</div> |
|||
<div><i class="fas fa-dice fa-fw"></i> Luck <span id="statsLuckDays{{=pool}}">{{=it.stats.pools[pool].luckDays}}</span> Days</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="pure-u-1-2"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Network Stats</div> |
|||
<div class="boxStatsList"> |
|||
<div> |
|||
<div><i class="fas fa-link fa-fw" aria-hidden="true"></i> Block Height: <span id="statsNetworkBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.networkBlocks}}</span></div> |
|||
<div><i class="fas fa-tachometer-alt fa-fw"></i> Network Hash/s: <span id="statsNetworkSols{{=pool}}">{{=it.stats.pools[pool].poolStats.networkSolsString}}</span></div> |
|||
<div><i class="fas fa-unlock-alt fa-fw" aria-hidden="true"></i> Difficulty: <span id="statsNetworkDiff{{=pool}}">{{=it.stats.pools[pool].poolStats.networkDiff}}</span></div> |
|||
<div><i class="fas fa-users fa-fw"></i> Node Connections: <span id="statsNetworkConnections{{=pool}}">{{=it.stats.pools[pool].poolStats.networkConnections}}</span></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
|||
</div> |
|||
|
|||
{{ for(var pool in it.stats.pools) { }} |
|||
{{ var blockscomb = new Array; }} |
|||
<div class="pure-g-r" id="boxesLower"> |
|||
<div class="pure-u-1-1"> |
|||
<div class="boxStats" id="boxStatsRight"> |
|||
<div class="boxLowerHeader">{{=it.stats.pools[pool].name}} Blocks Found |
|||
<span style="float:right;"><small> |
|||
<i class="fas fa-link fa-fw"></i> <span id="statsValidBlocks{{=pool}}">{{=it.stats.pools[pool].poolStats.validBlocks}}</span> Blocks |
|||
<i class="fas fa-money-bill-wave fa-fw"></i> Paid: <span id="statsTotalPaid{{=pool}}">{{=(parseFloat(it.stats.pools[pool].poolStats.totalPaid)).toFixed(8)}}</span> {{=it.stats.pools[pool].symbol}}</small> </span> |
|||
</div> |
|||
|
|||
<div class="boxStatsList" style="margin-top: 9px;"> |
|||
<!--<div id="{{=it.stats.pools[pool].name}}NewBlocks"></div>--> |
|||
{{ for(var b in it.stats.pools[pool].pending.blocks) { }} |
|||
{{ var block = it.stats.pools[pool].pending.blocks[b].split(":"); }} |
|||
<div style="margin-bottom: 9px; background-color: #773938; min-width:600px;" title="{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }}{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }}Waiting for dPoW notarization{{} else if (it.stats.pools[pool].pending.confirms[block[0]] < it.poolsConfigs[pool].paymentProcessing.minConf*2) { }}Waiting for min confirmations{{ } else { }}Queued for payment{{ } }}{{ } else { }}Waiting for payment processor to review{{ } }}"> |
|||
<div> |
|||
<i class="fas fa-link fa-fw"></i> |
|||
<small>Block:</small> |
|||
<a href="https://explorer.pirate.black/block/{{=block[0]}}" target="_blank" rel="noopener noreferrer">{{=block[2]}}</a> |
|||
|
|||
{{if (block[4] != null) { }} |
|||
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
{{if (it.stats.pools[pool].pending.confirms && it.stats.pools[pool].pending.confirms[block[0]]) { }} |
|||
{{if (it.stats.pools[pool].pending.confirms[block[0]] == 1) { }} |
|||
<span style="float:right; color: red;"><small>Waiting for Notarization</small></span> |
|||
{{ } else { }} |
|||
<span style="float:right; color: red;"><small>{{=it.stats.pools[pool].pending.confirms[block[0]]}} of {{=it.poolsConfigs[pool].paymentProcessing.minConf*2}} Confirmations</small></span> |
|||
{{ } }} |
|||
{{ } else { }} |
|||
<span style="float:right; color: red;"><small>*PENDING*</small></span> |
|||
{{ } }} |
|||
</div> |
|||
<div><i class="fas fa-crown fa-fw"></i> <small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div> |
|||
</div> |
|||
{{ blockscomb.push(block);}} |
|||
{{ } }} |
|||
|
|||
{{ var i=0; for(var b in it.stats.pools[pool].confirmed.blocks) { }} |
|||
{{ if (i < 8) { i++; }} |
|||
{{ var block = it.stats.pools[pool].confirmed.blocks[b].split(":"); }} |
|||
<div style="margin-bottom: 9px; background-color: #C99631; min-width:600px;" title="Payment sent, please check payments page"> |
|||
<div> |
|||
<i class="fas fa-link fa-fw"></i> |
|||
<small>Block:</small> |
|||
<a href="https://explorer.pirate.black/block/{{=block[0]}}" target="_blank" rel="noopener noreferrer">{{=block[2]}}</a> |
|||
{{if (block[4] != null) { }} |
|||
<span style="padding-left: 18px;"><small>{{=readableDate(block[4])}}</small></span> |
|||
{{ } }} |
|||
<span style="float:right; padding-left: 18px; color: green;"><small>*PAID*</small></span> |
|||
</div> |
|||
<div><i class="fas fa-crown fa-fw"></i> <small>Mined By:</small> <a href="/workers/{{=block[3].split('.')[0]}}">{{=block[3].length > 40 ? block[3].substring(0, 20) + '...' + block[3].substring(block[3].length-20, block[3].length): block[3]}}</a></div> |
|||
</div> |
|||
{{blockscomb.push(block);}} |
|||
{{ } }} |
|||
{{ } }} |
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{{if (blockscomb.length > 0) { }} |
|||
<div id="bottomCharts{{=pool}}" style="text-align:center;" align="center"> |
|||
<div class="chartWrapper" style="text-align:center;"> |
|||
<div class="chartLabel">Finders of the last {{=blockscomb.length}} blocks</div> |
|||
<div class="chartHolder" id="pie{{=pool}}"><svg id="blocksPie{{=pool}}" style="display: block; margin: auto; text-align:center;"/></div> |
|||
<div class="hidden tooltip" id="tooltip{{=pool}}"><p><span id="value{{=pool}}"></span> blocks found by <span id="finderr{{=pool}}"></span></p></div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script> |
|||
var blockscomb = ({{=JSON.stringify(blockscomb)}}) |
|||
var groupedByFinder = {}; |
|||
var data = []; |
|||
|
|||
for (var i=0; i < blockscomb.length; i++) { |
|||
finder=blockscomb[i][3]; // if other doesn 't already have a property for the current letter |
|||
// create it and assign it to a new empty array |
|||
if (!(finder in groupedByFinder)) |
|||
groupedByFinder[finder] = []; |
|||
|
|||
groupedByFinder[finder].push(blockscomb[i]); |
|||
} |
|||
|
|||
Object.keys(groupedByFinder).forEach(function(i) { |
|||
var obj = {}; |
|||
obj.label = i |
|||
obj.value = groupedByFinder[i].length |
|||
data.push(obj) |
|||
}); |
|||
|
|||
//console.log(JSON.stringify(data)) |
|||
|
|||
var w = 1000; |
|||
var h = 500; |
|||
var r = h/2.5; |
|||
var legendRectSize = 18; |
|||
var legendSpacing = 5; |
|||
|
|||
var color = d3.scale.category20c(); |
|||
/*var div = d3.select("#pie{{=pool}}").append("div") |
|||
.attr("class", "tooltip") |
|||
.style("opacity", 0);*/ |
|||
|
|||
var vis = d3.select('#blocksPie{{=pool}}') |
|||
.data([data]) |
|||
.attr("width", w) |
|||
.attr("height", h) |
|||
.attr("style", "display: block; margin: auto; width:800px; height: auto; display:inline-block") |
|||
.attr("preserveAspectRatio", "xMidYMin") |
|||
.append("svg:g") |
|||
.attr("transform", "translate(" + r + "," + r + ")"); |
|||
|
|||
|
|||
var pie = d3.layout.pie().value(function(d){return d.value;}); |
|||
|
|||
// declare an arc generator function |
|||
var arc = d3.svg.arc().outerRadius(r); |
|||
|
|||
// select paths, use arc generator to draw |
|||
var arcs = vis.selectAll("g.slice{{=pool}}") |
|||
.data(pie) |
|||
.enter() |
|||
.append("svg:g") |
|||
.attr("class", "slice{{=pool}}") |
|||
.attr("id", "slice") |
|||
.on("mouseover", function(d){ |
|||
d3.select("#tooltip{{=pool}}") |
|||
.attr("style", "left:" + (d3.event.layerX) + "px; top:" + (d3.event.layerY) + "px; opacity:1; display:block!important;position:absolute") |
|||
.select("#value{{=pool}}") |
|||
.text(d.data.value); |
|||
d3.select("#tooltip{{=pool}}") |
|||
.select("#finderr{{=pool}}") |
|||
.text(d.data.label.length > 40 ? d.data.label.substring(0, 20) + '...' + d.data.label.substring(d.data.label.length-20, d.data.label.length): d.data.label); |
|||
}) |
|||
.on("mouseout", function(d) { |
|||
d3.select("#tooltip{{=pool}}") |
|||
.attr("style", ""); |
|||
}) |
|||
.on('mousemove', function(d) { |
|||
d3.select("#tooltip{{=pool}}") |
|||
.style('top', (d3.event.layerY) + 'px') |
|||
.style('left', (d3.event.layerX) + 'px'); |
|||
}); |
|||
|
|||
arcs.append("svg:path") |
|||
.attr("fill", function(d, i){ |
|||
return color(i); |
|||
}) |
|||
.attr("d", function (d) { |
|||
return arc(d); |
|||
}); |
|||
|
|||
var legend = vis.selectAll('.legend') |
|||
.data(color.domain()) |
|||
.enter() |
|||
.append('g') |
|||
.attr('class', 'legend') |
|||
.attr('id', {{=JSON.stringify(pool)}}) |
|||
.attr('transform', function(d, i) { |
|||
var height = legendRectSize + legendSpacing; |
|||
var offset = height * color.domain().length / 2; |
|||
var horz = 13 * legendRectSize; |
|||
var vert = (i * height) - r + height; |
|||
return 'translate(' + horz + ',' + vert + ')'; |
|||
}); |
|||
|
|||
legend.append('rect') |
|||
.attr('width', legendRectSize) |
|||
.attr('height', legendRectSize) |
|||
.style('fill', color) |
|||
.style('stroke', color); |
|||
|
|||
legend.append('text') |
|||
.attr('x', legendRectSize + legendSpacing) |
|||
.attr('y', legendRectSize - legendSpacing) |
|||
.text(function(d, i) { |
|||
return data[i].label.length > 40 ? data[i].label.substring(0, 20) + '...' + data[i].label.substring(data[i].label.length-20, data[i].label.length): data[i].label; |
|||
}); |
|||
</script> |
|||
{{ } }} |
|||
{{ } }} |
|||
|
|||
<script> |
|||
document.querySelector('main').appendChild(document.createElement('script')).src = '/static/stats.js'; |
|||
|
|||
$(function() { |
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
var stats = JSON.parse(e.data); |
|||
for (var pool in stats.pools) { |
|||
$('#statsMiners' + pool).text(stats.pools[pool].minerCount); |
|||
$('#statsWorkers' + pool).text(stats.pools[pool].workerCount); |
|||
$('#statsHashrate' + pool).text(stats.pools[pool].hashrateString); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
$('#statsLuckDays' + pool).text(stats.pools[pool].luckDays); |
|||
$('#statsValidBlocks' + pool).text(stats.pools[pool].poolStats.validBlocks); |
|||
$('#statsTotalPaid' + pool).text((parseFloat(stats.pools[pool].poolStats.totalPaid)).toFixed(8)); |
|||
$('#statsNetworkBlocks' + pool).text(stats.pools[pool].poolStats.networkBlocks); |
|||
$('#statsNetworkDiff' + pool).text(stats.pools[pool].poolStats.networkDiff); |
|||
$('#statsNetworkSols' + pool).text(getReadableNetworkHashRateString(stats.pools[pool].poolStats.networkSols)); |
|||
$('#statsNetworkConnections' + pool).text(stats.pools[pool].poolStats.networkConnections); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function getReadableNetworkHashRateString(hashrate){ |
|||
hashrate = (hashrate * 1000000); |
|||
if (hashrate < 1000000) |
|||
return '0 Sol'; |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
return hashrate.toFixed(2) + byteUnits[i]; |
|||
} |
|||
</script> |
@ -0,0 +1,30 @@ |
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Pool</th> |
|||
<th>Algo</th> |
|||
<th>Workers</th> |
|||
<th>Valid Shares</th> |
|||
<th>Invalid Shares</th> |
|||
<th>Total Blocks</th> |
|||
<th>Pending</th> |
|||
<th>Confirmed</th> |
|||
<th>Orphaned</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var pool in it.stats.pools) { }} |
|||
<tr> |
|||
<td>{{=it.stats.pools[pool].name}}</td> |
|||
<td>{{=it.stats.pools[pool].algorithm}}</td> |
|||
<td>{{=Object.keys(it.stats.pools[pool].workers).length}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.invalidShares}}</td> |
|||
<td>{{=it.stats.pools[pool].poolStats.validBlocks}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.pending}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.confirmed}}</td> |
|||
<td>{{=it.stats.pools[pool].blocks.orphaned}}</td> |
|||
<td>{{=it.stats.pools[pool].hashrateString}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
@ -0,0 +1,60 @@ |
|||
<script type="text/javascript"> |
|||
function searchKeyPress(e) |
|||
{ |
|||
// look for window.event in case event isn't passed in |
|||
e = e || window.event; |
|||
if (e.keyCode == 13) |
|||
{ |
|||
document.getElementById('btnSearch').click(); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
$(document).ready(function(){ |
|||
$('.btn-lg').click(function(){ |
|||
window.location = "workers/" + $('.input-lg').val(); |
|||
}); |
|||
}); |
|||
</script> |
|||
{{ function capitalizeFirstLetter(t){return t.charAt(0).toUpperCase()+t.slice(1)} }} |
|||
{{ var i=0; for(var pool in it.stats.pools) { }} |
|||
<div id="topPool"> |
|||
<div class="poolWrapper"> |
|||
<div class="poolLabel"> |
|||
<span style="float:right; margin-bottom: 8px;"> |
|||
<small>Miner Lookup: |
|||
<input type="text" class="form-control input-lg" onkeypress="return searchKeyPress(event);"> |
|||
<span class="input-group-btn"> |
|||
<button class="btn btn-default btn-lg" type="button">Lookup</button> |
|||
</span> |
|||
</small> |
|||
</span> |
|||
{{=capitalizeFirstLetter(it.stats.pools[pool].name)}} Top Miners |
|||
<small><i class="fas fa-users fa-fw"></i> <span id="statsMiners{{=pool}}">{{=it.stats.pools[pool].minerCount}}</span> Miners |
|||
<i class="fas fa-rocket fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].workerCount}}</span> Workers |
|||
<i class="fas fa-cog fa-fw"></i> <span id="statsWorkers{{=pool}}">{{=it.stats.pools[pool].shareCount}}</span> Shares </small> |
|||
</div> |
|||
<div class="poolMinerTable"> |
|||
<table class="pure-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>Address</th> |
|||
<th>Shares</th> |
|||
<th>Efficiency</th> |
|||
<th>Hashrate</th> |
|||
</tr> |
|||
</thead> |
|||
{{ for(var worker in it.stats.pools[pool].miners) { }} |
|||
{{var workerstat = it.stats.pools[pool].miners[worker];}} |
|||
<tr class="pure-table-odd"> |
|||
<td><a href="/workers/{{=worker.split('.')[0]}}">{{=worker.length > 40 ? worker.substring(0, 20) + '...' + worker.substring(worker.length-20, worker.length): worker}}</a></td> |
|||
<td>{{=Math.round(workerstat.currRoundShares * 100) / 100}}</td> |
|||
<td>{{? workerstat.shares > 0}} {{=Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100}}% {{??}} 0% {{?}}</td> |
|||
<td>{{=workerstat.hashrateString}}</td> |
|||
</tr> |
|||
{{ } }} |
|||
</table> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{{ } }} |
@ -0,0 +1,100 @@ |
|||
var docCookies = { |
|||
getItem: function (sKey) { |
|||
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; |
|||
}, |
|||
setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { |
|||
if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } |
|||
var sExpires = ""; |
|||
if (vEnd) { |
|||
switch (vEnd.constructor) { |
|||
case Number: |
|||
sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; |
|||
break; |
|||
case String: |
|||
sExpires = "; expires=" + vEnd; |
|||
break; |
|||
case Date: |
|||
sExpires = "; expires=" + vEnd.toUTCString(); |
|||
break; |
|||
} |
|||
} |
|||
document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); |
|||
return true; |
|||
}, |
|||
removeItem: function (sKey, sPath, sDomain) { |
|||
if (!sKey || !this.hasItem(sKey)) { return false; } |
|||
document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + ( sDomain ? "; domain=" + sDomain : "") + ( sPath ? "; path=" + sPath : ""); |
|||
return true; |
|||
}, |
|||
hasItem: function (sKey) { |
|||
return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); |
|||
} |
|||
}; |
|||
|
|||
var password = docCookies.getItem('password'); |
|||
|
|||
|
|||
function showLogin(){ |
|||
$('#adminCenter').hide(); |
|||
$('#passwordForm').show(); |
|||
} |
|||
|
|||
function showAdminCenter(){ |
|||
$('#passwordForm').hide(); |
|||
$('#adminCenter').show(); |
|||
} |
|||
|
|||
function tryLogin(){ |
|||
apiRequest('pools', {}, function(response){ |
|||
showAdminCenter(); |
|||
displayMenu(response.result) |
|||
}); |
|||
} |
|||
|
|||
function displayMenu(pools){ |
|||
$('#poolList').after(Object.keys(pools).map(function(poolName){ |
|||
return '<li class="poolMenuItem"><a href="#">' + poolName + '</a></li>'; |
|||
}).join('')); |
|||
} |
|||
|
|||
function apiRequest(func, data, callback){ |
|||
var httpRequest = new XMLHttpRequest(); |
|||
httpRequest.onreadystatechange = function(){ |
|||
if (httpRequest.readyState === 4 && httpRequest.responseText){ |
|||
if (httpRequest.status === 401){ |
|||
docCookies.removeItem('password'); |
|||
$('#password').val(''); |
|||
showLogin(); |
|||
alert('Incorrect Password'); |
|||
} |
|||
else{ |
|||
var response = JSON.parse(httpRequest.responseText); |
|||
callback(response); |
|||
} |
|||
} |
|||
}; |
|||
httpRequest.open('POST', '/api/admin/' + func); |
|||
data.password = password; |
|||
httpRequest.setRequestHeader('Content-Type', 'application/json'); |
|||
httpRequest.send(JSON.stringify(data)); |
|||
} |
|||
|
|||
if (password){ |
|||
tryLogin(); |
|||
} |
|||
else{ |
|||
showLogin(); |
|||
} |
|||
|
|||
$('#passwordForm').submit(function(event){ |
|||
event.preventDefault(); |
|||
password = $('#password').val(); |
|||
if (password){ |
|||
if ($('#remember').is(':checked')) |
|||
docCookies.setItem('password', password, Infinity); |
|||
else |
|||
docCookies.setItem('password', password); |
|||
tryLogin(); |
|||
} |
|||
return false; |
|||
}); |
After Width: | Height: | Size: 413 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 4.9 KiB |
@ -0,0 +1,2 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<browserconfig><msapplication><tile><square70x70logo src="/static/favicon/ms-icon-70x70.png"/><square150x150logo src="/static/favicon/ms-icon-150x150.png"/><square310x310logo src="/static/favicon/ms-icon-310x310.png"/><TileColor>#BB9645</TileColor></tile></msapplication></browserconfig> |
After Width: | Height: | Size: 641 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,41 @@ |
|||
{ |
|||
"name": "App", |
|||
"icons": [ |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-36x36.png", |
|||
"sizes": "36x36", |
|||
"type": "image\/png", |
|||
"density": "0.75" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-48x48.png", |
|||
"sizes": "48x48", |
|||
"type": "image\/png", |
|||
"density": "1.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-72x72.png", |
|||
"sizes": "72x72", |
|||
"type": "image\/png", |
|||
"density": "1.5" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-96x96.png", |
|||
"sizes": "96x96", |
|||
"type": "image\/png", |
|||
"density": "2.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-144x144.png", |
|||
"sizes": "144x144", |
|||
"type": "image\/png", |
|||
"density": "3.0" |
|||
}, |
|||
{ |
|||
"src": "\/static\/favicon\/android-icon-192x192.png", |
|||
"sizes": "192x192", |
|||
"type": "image\/png", |
|||
"density": "4.0" |
|||
} |
|||
] |
|||
} |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,145 @@ |
|||
var poolHashrateData; |
|||
var poolHashrateChart; |
|||
|
|||
var statData; |
|||
var poolKeys; |
|||
|
|||
function buildChartData(){ |
|||
var pools = {}; |
|||
|
|||
poolKeys = []; |
|||
for (var i = 0; i < statData.length; i++){ |
|||
for (var pool in statData[i].pools){ |
|||
if (poolKeys.indexOf(pool) === -1) |
|||
poolKeys.push(pool); |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < statData.length; i++) { |
|||
var time = statData[i].time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++){ |
|||
var pName = poolKeys[f]; |
|||
var a = pools[pName] = (pools[pName] || { |
|||
hashrate: [] |
|||
}); |
|||
if (pName in statData[i].pools){ |
|||
a.hashrate.push([time, statData[i].pools[pName].hashrate]); |
|||
} |
|||
else{ |
|||
a.hashrate.push([time, 0]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
poolHashrateData = []; |
|||
for (var pool in pools){ |
|||
poolHashrateData.push({ |
|||
key: pool, |
|||
values: pools[pool].hashrate |
|||
}); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
} |
|||
} |
|||
|
|||
function calculateAverageHashrate(pool) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < poolHashrateData[i].values.length; ii++) { |
|||
if (pool == null || poolHashrateData[i].key === pool) { |
|||
count++; |
|||
avg += parseFloat(poolHashrateData[i].values[ii][1]); |
|||
} |
|||
} |
|||
if (count > total) |
|||
total = count; |
|||
} |
|||
avg = avg / total; |
|||
return avg; |
|||
} |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
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 displayCharts(){ |
|||
nv.addGraph(function() { |
|||
poolHashrateChart = nv.models.lineChart() |
|||
.margin({left: 80, right: 30}) |
|||
.x(function(d){ return d[0] }) |
|||
.y(function(d){ return d[1] }) |
|||
.useInteractiveGuideline(true); |
|||
|
|||
poolHashrateChart.xAxis.tickFormat(timeOfDayFormat); |
|||
|
|||
poolHashrateChart.yAxis.tickFormat(function(d){ |
|||
return getReadableHashRateString(d); |
|||
}); |
|||
|
|||
d3.select('#poolHashrate').datum(poolHashrateData).call(poolHashrateChart); |
|||
|
|||
return poolHashrateChart; |
|||
}); |
|||
} |
|||
|
|||
function triggerChartUpdates(){ |
|||
poolHashrateChart.update(); |
|||
} |
|||
|
|||
nv.utils.windowResize(triggerChartUpdates); |
|||
|
|||
$.getJSON('/api/pool_stats', function(data){ |
|||
statData = data; |
|||
buildChartData(); |
|||
}); |
|||
|
|||
$(function() { |
|||
statsSource.addEventListener('message', function(e){ |
|||
var stats = JSON.parse(e.data); |
|||
statData.push(stats); |
|||
|
|||
var newPoolAdded = (function(){ |
|||
for (var p in stats.pools){ |
|||
if (poolKeys.indexOf(p) === -1) |
|||
return true; |
|||
} |
|||
return false; |
|||
})(); |
|||
|
|||
if (newPoolAdded || Object.keys(stats.pools).length > poolKeys.length){ |
|||
buildChartData(); |
|||
displayCharts(); |
|||
} |
|||
else { |
|||
var time = stats.time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
if (poolHashrateData[i].key === pool) { |
|||
poolHashrateData[i].values.shift(); |
|||
poolHashrateData[i].values.push([time, pool in stats.pools ? stats.pools[pool].hashrate : 0]); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
//triggerChartUpdates();
|
|||
} |
|||
}); |
|||
|
|||
}); |
After Width: | Height: | Size: 797 B |
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,34 @@ |
|||
$(function(){ |
|||
|
|||
var hotSwap = function(page, pushSate){ |
|||
if (pushSate) history.pushState(null, null, '/' + page); |
|||
$('.pure-menu-selected').removeClass('pure-menu-selected'); |
|||
if (page.length > 0) { |
|||
$('a[href="/' + page + '"]').parent().addClass('pure-menu-selected'); |
|||
} else { |
|||
$('a[href="/"]').addClass('pure-menu-selected'); |
|||
} |
|||
$.get("/get_page", {id: page}, function(data){ |
|||
$('main').html(data); |
|||
}, 'html') |
|||
}; |
|||
|
|||
$('.hot-swapper').click(function(event){ |
|||
if (event.which !== 1) return; |
|||
var pageId = $(this).attr('href').slice(1); |
|||
hotSwap(pageId, true); |
|||
event.preventDefault(); |
|||
return false; |
|||
}); |
|||
|
|||
window.addEventListener('load', function() { |
|||
setTimeout(function() { |
|||
window.addEventListener("popstate", function(e) { |
|||
hotSwap(location.pathname.slice(1)); |
|||
}); |
|||
}, 0); |
|||
}); |
|||
|
|||
window.statsSource = new EventSource("/api/live_stats"); |
|||
|
|||
}); |
@ -0,0 +1,247 @@ |
|||
var workerHashrateData; |
|||
var workerHashrateChart; |
|||
var workerHistoryMax = 160; |
|||
|
|||
var statData; |
|||
var totalHash; |
|||
var totalImmature; |
|||
var totalBal; |
|||
var totalPaid; |
|||
var totalShares; |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
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: [] |
|||
}); |
|||
for (var wh in statData.history[w]) { |
|||
a.hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); |
|||
} |
|||
if (a.hashrate.length > workerHistoryMax) { |
|||
workerHistoryMax = a.hashrate.length; |
|||
} |
|||
} |
|||
|
|||
var i=0; |
|||
workerHashrateData = []; |
|||
for (var worker in workers){ |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
//disabled: (i > Math.min((_workerCount-1), 3)),
|
|||
disabled: false, |
|||
values: workers[worker].hashrate |
|||
}); |
|||
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 wh = statData.history[w][statData.history[w].length - 1];
|
|||
var foundWorker = false; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
if (workerHashrateData[i].key === worker) { |
|||
foundWorker = 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]); |
|||
break; |
|||
} |
|||
} |
|||
if (!foundWorker) { |
|||
var hashrate = []; |
|||
hashrate.push([statData.history[w][wh].time * 1000, statData.history[w][wh].hashrate]); |
|||
workerHashrateData.push({ |
|||
key: worker, |
|||
values: hashrate |
|||
}); |
|||
rebuildWorkerDisplay(); |
|||
return true; |
|||
} |
|||
} |
|||
triggerChartUpdates(); |
|||
return false; |
|||
} |
|||
|
|||
function calculateAverageHashrate(worker) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < workerHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < workerHashrateData[i].values.length; ii++) { |
|||
if (worker == null || workerHashrateData[i].key === worker) { |
|||
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; |
|||
totalPaid = statData.paid; |
|||
totalBal = statData.balance; |
|||
totalImmature = statData.immature; |
|||
totalShares = statData.totalShares; |
|||
// do some calculations
|
|||
var _blocktime = 250; |
|||
var _networkHashRate = parseFloat(statData.networkSols) * 1.2; |
|||
var _myHashRate = (totalHash / 1000000) * 2; |
|||
var luckDays = ((_networkHashRate / _myHashRate * _blocktime) / (24 * 60 * 60)).toFixed(3); |
|||
// update miner stats
|
|||
$("#statsHashrate").text(getReadableHashRateString(totalHash)); |
|||
$("#statsHashrateAvg").text(getReadableHashRateString(calculateAverageHashrate(null))); |
|||
$("#statsLuckDays").text(luckDays); |
|||
$("#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].hashrate)); |
|||
$("#statsHashrateAvg"+htmlSafeWorkerName).text(getReadableHashRateString(calculateAverageHashrate(saneWorkerName))); |
|||
$("#statsLuckDays"+htmlSafeWorkerName).text(statData.workers[w].luckDays); |
|||
$("#statsPaid"+htmlSafeWorkerName).text(statData.workers[w].paid); |
|||
$("#statsBalance"+htmlSafeWorkerName).text(statData.workers[w].balance); |
|||
$("#statsShares"+htmlSafeWorkerName).text(Math.round(statData.workers[w].currRoundShares * 100) / 100); |
|||
$("#statsDiff"+htmlSafeWorkerName).text(statData.workers[w].diff); |
|||
} |
|||
} |
|||
function addWorkerToDisplay(name, htmlSafeName, workerObj) { |
|||
var htmlToAdd = ""; |
|||
htmlToAdd = '<div class="boxStats" id="boxStatsLeft" style="float:left; margin: 9px; min-width: 260px;"><div class="boxStatsList">'; |
|||
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><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrate'+htmlSafeName+'">'+getReadableHashRateString(workerObj.hashrate)+'</span> (Now)</div>'; |
|||
htmlToAdd+='<div><i class="fas fa-tachometer-alt fa-fw"></i> <span id="statsHashrateAvg'+htmlSafeName+'">'+getReadableHashRateString(calculateAverageHashrate(name))+'</span> (Avg)</div>'; |
|||
htmlToAdd+='<div><i class="fas fa-unlock-alt fa-fw"></i> <small>Diff:</small> <span id="statsDiff'+htmlSafeName+'">'+workerObj.diff+'</span></div>'; |
|||
htmlToAdd+='<div><i class="fas fa-cog fa-fw"></i> <small>Shares:</small> <span id="statsShares'+htmlSafeName+'">'+(Math.round(workerObj.currRoundShares * 100) / 100)+'</span></div>'; |
|||
htmlToAdd+='<div><i class="fas fa-dice fa-fw"></i> <small>Luck <span id="statsLuckDays'+htmlSafeName+'">'+workerObj.luckDays+'</span> Days</small></div>'; |
|||
htmlToAdd+='<div><i class="fas fa-money-bill-wave fa-fw"></i> <small>Bal: <span id="statsBalance'+htmlSafeName+'">'+workerObj.balance+'</span></small></div>'; |
|||
htmlToAdd+='<div><i class="fas fa-money-bill-wave fa-fw"></i> <small>Paid: <span id="statsPaid'+htmlSafeName+'">'+workerObj.paid+'</span></small></div>'; |
|||
htmlToAdd+='</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(); |
|||
} |
|||
}); |
|||
}); |
@ -0,0 +1,84 @@ |
|||
$(function() { |
|||
initStatData(); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
if (document.querySelector('#pagePayments') !== null) { |
|||
var stats = JSON.parse(e.data); |
|||
|
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
|
|||
for (var i = 0; i < stats.pools[pool].payments.length; i++) { |
|||
var paymentstat = stats.pools[pool].payments[i]; |
|||
|
|||
var existingRow = document.querySelector('#payment' + pool + paymentstat.time); |
|||
|
|||
if (existingRow == null) { |
|||
clearInterval(nextPaymentTimer); |
|||
paymentTimerOn = false; |
|||
|
|||
//Add new
|
|||
var insertPaymentTr = document.createElement('tr'); |
|||
insertPaymentTr.id = 'payment' + pool + paymentstat.time; |
|||
insertPaymentTr.setAttribute('class', 'dynamicallyInserted'); |
|||
insertPaymentTr.style.opacity = 0; |
|||
insertPaymentTr.style.transition = 'opacity 1s ease-in'; |
|||
|
|||
if (typeof paymentstat.txid !== 'undefined') { |
|||
var explorerlink = '<a href="' + explorerURL + 'tx/' + paymentstat.txid + '" target="_blank" rel="noopener noreferrer">' + paymentstat.blocks + '</a>'; |
|||
} else { |
|||
var explorerlink = '<a>' + paymentstat.blocks + '</a>'; |
|||
} |
|||
|
|||
insertPaymentTr.innerHTML = '<td class="paymentblocks" title="Blocks:' + paymentstat.blocks.length + ' ' + paymentstat.opid + '">' |
|||
+ '<span class="responsiveTableLabel"><i class="fas fa-link fa-fw"></i></span> <span>Blocks: [' + paymentstat.blocks.length + '] </span>' |
|||
+ explorerlink + '<div class="fade">▼</div></td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="far fa-clock fa-fw"></i> Time: </span>' + readableDate(paymentstat.time) + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-users fa-fw"></i> Miners: </span>' + paymentstat.miners + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span>' + bigNumber(paymentstat.shares) + '</td>'; |
|||
insertPaymentTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-money-bill-wave fa-fw"></i> Amount: </span>' + paymentstat.paid + ' ' + stats.pools[pool].symbol + '</td>'; |
|||
|
|||
var paymentTable = document.querySelector('#paymentTable' + pool + ' tbody'); |
|||
if (paymentTable != null) { |
|||
paymentTable.insertBefore(insertPaymentTr, paymentTable.firstChild); |
|||
setTimeout(() => { |
|||
document.querySelectorAll('.dynamicallyInserted').forEach(function(newPayment) { |
|||
newPayment.style.opacity = 1; |
|||
}); |
|||
}, 25); |
|||
console.log('Added new payment!'); |
|||
} |
|||
} else { |
|||
//Update existing (txid) for private chains
|
|||
if (typeof paymentstat.txid !== 'undefined' && (String(stats.pools[pool].name).startsWith("pirate") || String(stats.pools[pool].name).startsWith("arrr")) ) { |
|||
var explorer = 'https://explorer.pirate.black/tx/'; |
|||
var paymentblock = document.querySelector('#payment' + pool + paymentstat.time + ' .paymentblocks a'); |
|||
|
|||
paymentblock.setAttribute('href', explorer + paymentstat.txid); |
|||
paymentblock.setAttribute('target', '_blank'); |
|||
paymentblock.setAttribute('rel', 'noopener noreferrer'); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Global var from main.js - cleared on initStatData(), this needs to be started after the loop in case a new payment was added and old timer needs to be reset
|
|||
if (!paymentTimerOn) { |
|||
nextPaymentTimer = setInterval(function() { |
|||
var timeElement = document.querySelector('#statsNextPayment' + pool); |
|||
if (timeElement !== null) { |
|||
var timeleft=(paymentInterval-parseInt((new Date().getTime() - parseInt(stats.pools[pool].payments[0].time))/1000)); |
|||
if (timeleft > 0) { |
|||
timeElement.innerHTML = timeTil(timeleft); |
|||
timeElement.setAttribute('title', timeTilNumbers(timeleft)); |
|||
} else { |
|||
timeElement.innerHTML = 'Now'; |
|||
timeElement.setAttribute('title', '00:00:00'); |
|||
} |
|||
} |
|||
}, 1000); |
|||
paymentTimerOn = true; |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 9.4 KiB |
@ -0,0 +1,143 @@ |
|||
var poolHashrateData; |
|||
var poolHashrateChart; |
|||
|
|||
var statData; |
|||
var poolKeys; |
|||
|
|||
function buildChartData(){ |
|||
var pools = {}; |
|||
|
|||
poolKeys = []; |
|||
for (var i = 0; i < statData.length; i++){ |
|||
for (var pool in statData[i].pools){ |
|||
if (poolKeys.indexOf(pool) === -1) |
|||
poolKeys.push(pool); |
|||
} |
|||
} |
|||
|
|||
for (var i = 0; i < statData.length; i++) { |
|||
var time = statData[i].time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++){ |
|||
var pName = poolKeys[f]; |
|||
var a = pools[pName] = (pools[pName] || { |
|||
hashrate: [] |
|||
}); |
|||
if (pName in statData[i].pools){ |
|||
a.hashrate.push([time, statData[i].pools[pName].hashrate]); |
|||
} |
|||
else{ |
|||
a.hashrate.push([time, 0]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
poolHashrateData = []; |
|||
for (var pool in pools){ |
|||
poolHashrateData.push({ |
|||
key: pool, |
|||
values: pools[pool].hashrate |
|||
}); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
} |
|||
} |
|||
|
|||
function calculateAverageHashrate(pool) { |
|||
var count = 0; |
|||
var total = 1; |
|||
var avg = 0; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
count = 0; |
|||
for (var ii = 0; ii < poolHashrateData[i].values.length; ii++) { |
|||
if (pool == null || poolHashrateData[i].key === pool) { |
|||
count++; |
|||
avg += parseFloat(poolHashrateData[i].values[ii][1]); |
|||
} |
|||
} |
|||
if (count > total) |
|||
total = count; |
|||
} |
|||
avg = avg / total; |
|||
return avg; |
|||
} |
|||
|
|||
function getReadableHashRateString(hashrate){ |
|||
hashrate = (hashrate * 2); |
|||
if (hashrate < 1000000) { |
|||
return (Math.round(hashrate / 1000) / 1000 ).toFixed(2)+' Sol/s'; |
|||
} |
|||
var byteUnits = [ ' Sol/s', ' KSol/s', ' MSol/s', ' GSol/s', ' TSol/s', ' PSol/s' ]; |
|||
var i = Math.floor((Math.log(hashrate/1000) / Math.log(1000)) - 1); |
|||
hashrate = (hashrate/1000) / Math.pow(1000, i + 1); |
|||
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 displayCharts(){ |
|||
nv.addGraph(function() { |
|||
poolHashrateChart = nv.models.lineChart() |
|||
.margin({left: 80, right: 30}) |
|||
.x(function(d){ return d[0] }) |
|||
.y(function(d){ return d[1] }) |
|||
.useInteractiveGuideline(true); |
|||
|
|||
poolHashrateChart.xAxis.tickFormat(timeOfDayFormat); |
|||
|
|||
poolHashrateChart.yAxis.tickFormat(function(d){ |
|||
return getReadableHashRateString(d); |
|||
}); |
|||
|
|||
d3.select('#poolHashrate').datum(poolHashrateData).call(poolHashrateChart); |
|||
|
|||
return poolHashrateChart; |
|||
}); |
|||
} |
|||
|
|||
function triggerChartUpdates(){ |
|||
poolHashrateChart.update(); |
|||
} |
|||
|
|||
nv.utils.windowResize(triggerChartUpdates); |
|||
|
|||
$.getJSON('/api/pool_stats', function(data){ |
|||
statData = data; |
|||
buildChartData(); |
|||
displayCharts(); |
|||
}); |
|||
|
|||
statsSource.addEventListener('message', function(e){ |
|||
var stats = JSON.parse(e.data); |
|||
statData.push(stats); |
|||
|
|||
var newPoolAdded = (function(){ |
|||
for (var p in stats.pools){ |
|||
if (poolKeys.indexOf(p) === -1) |
|||
return true; |
|||
} |
|||
return false; |
|||
})(); |
|||
|
|||
if (newPoolAdded || Object.keys(stats.pools).length > poolKeys.length){ |
|||
buildChartData(); |
|||
displayCharts(); |
|||
} |
|||
else { |
|||
var time = stats.time * 1000; |
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
for (var i = 0; i < poolHashrateData.length; i++) { |
|||
if (poolHashrateData[i].key === pool) { |
|||
poolHashrateData[i].values.shift(); |
|||
poolHashrateData[i].values.push([time, pool in stats.pools ? stats.pools[pool].hashrate : 0]); |
|||
$('#statsHashrateAvg' + pool).text(getReadableHashRateString(calculateAverageHashrate(pool))); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
triggerChartUpdates(); |
|||
} |
|||
}); |
@ -0,0 +1,612 @@ |
|||
|
|||
html, body, button, input, select, textarea, .pure-g [class *= "pure-u"], .pure-g-r [class *= "pure-u"]{ |
|||
font-family: 'Open Sans', sans-serif; |
|||
} |
|||
|
|||
body{ |
|||
background-color:#000; |
|||
display: flex; |
|||
flex-direction: column; |
|||
max-width: 1160px; |
|||
margin: 0 auto; |
|||
} |
|||
|
|||
header > .home-menu { |
|||
background: inherit !important; |
|||
/* height: 54px; */ |
|||
padding: 10px 0; |
|||
display: flex; |
|||
} |
|||
|
|||
header > .home-menu > a.pure-menu-heading, header > .home-menu > ul, header > .home-menu > ul > li{ |
|||
display: flex !important; |
|||
align-items: center; |
|||
justify-content: center; |
|||
line-height: normal !important; |
|||
flex-wrap:wrap; |
|||
} |
|||
|
|||
header > .home-menu a { |
|||
outline:none; |
|||
} |
|||
|
|||
header > .home-menu > a.pure-menu-heading { |
|||
color: #b1de5a; |
|||
font-size: 1.5em; |
|||
} |
|||
|
|||
header > .home-menu > ul > li > a { |
|||
color: #b1de5a; |
|||
} |
|||
|
|||
header > .home-menu > ul > li > a:hover, |
|||
header > .home-menu > ul > li > a:focus{ |
|||
background: inherit !important; |
|||
} |
|||
|
|||
header > .home-menu > a.pure-menu-heading.pure-menu-selected, |
|||
header > .home-menu > ul > li > a:hover, |
|||
header > .home-menu > ul > li.pure-menu-selected > a { |
|||
color: #FCAD4C; |
|||
} |
|||
|
|||
a, a:link, a:visited { |
|||
color:#FFF; |
|||
} |
|||
|
|||
main{ |
|||
background-color: #2d2d2d; |
|||
color:#FFF; |
|||
position: relative; |
|||
} |
|||
|
|||
footer{ |
|||
text-align: center; |
|||
color: #b3b3b3; |
|||
text-decoration: none; |
|||
font-size: 0.8em; |
|||
padding: 15px; |
|||
line-height: 24px; |
|||
} |
|||
|
|||
footer a{ |
|||
color: #fff; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
footer iframe{ |
|||
vertical-align: middle; |
|||
} |
|||
|
|||
code { |
|||
font-family: monospace, serif; |
|||
display:block; |
|||
margin:15px; |
|||
padding:20px; |
|||
background-color:#725F49; |
|||
} |
|||
|
|||
input, button { |
|||
color:#000; |
|||
} |
|||
|
|||
.content { |
|||
margin:15px; |
|||
} |
|||
|
|||
/* PURE TABLE */ |
|||
.pure-table thead { |
|||
background-color:#C99631; |
|||
} |
|||
|
|||
.pure-table-odd td, |
|||
.pure-table-even td { |
|||
background-color: #1F152A |
|||
} |
|||
|
|||
|
|||
/* PAGE STYLES */ |
|||
|
|||
/* HOME */ |
|||
#boxWelcome{ |
|||
background-color: #887705; |
|||
color: white; |
|||
margin: 18px; |
|||
} |
|||
#logoImg{ |
|||
height: 285px; |
|||
margin: 55px; |
|||
} |
|||
#welcomeText{ |
|||
font-size: 2.7em; |
|||
margin: 50px 18px 10px 18px; |
|||
} |
|||
#welcomeItems{ |
|||
list-style-type: none; |
|||
font-size: 1.3em; |
|||
padding: 0 !important; |
|||
margin: 0 0 0 18px !important; |
|||
} |
|||
#welcomeItems > li{ |
|||
margin: 30px !important; |
|||
} |
|||
#boxesLower { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesLower > div { |
|||
display: flex; |
|||
} |
|||
#boxesLower > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
|
|||
#boxStatsLeft, #boxStatsRight{ |
|||
background-color: #1F152A; |
|||
} |
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
/* align-content: center; */ |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
|||
|
|||
/* GETTING STARTED */ |
|||
#holder{ |
|||
display: flex; |
|||
flex-direction: row; |
|||
} |
|||
|
|||
.glow{ |
|||
box-shadow: inset 0 0 12px 4px #ff6c00; |
|||
} |
|||
|
|||
.hidden{ |
|||
display: none !important; |
|||
} |
|||
|
|||
#menu{ |
|||
background-color: #3d3d3d; |
|||
min-width: 170px; |
|||
width:100%; |
|||
} |
|||
|
|||
#menu > .menuHeader{ |
|||
color: #FFF; |
|||
font-weight:900; |
|||
border-bottom: 1px solid #7f878b; |
|||
font-size: 1.2em; |
|||
padding: 16px 16px 4px 15px; |
|||
} |
|||
|
|||
.menuList{ |
|||
transition-duration: 200ms; |
|||
} |
|||
|
|||
.menuList > a:first-child{ |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.menuList > a{ |
|||
display: inline-block; |
|||
color: #e3f7ff; |
|||
text-decoration: none; |
|||
padding: 7px; |
|||
padding-left: 25px; |
|||
width:40px; |
|||
} |
|||
|
|||
#main{ |
|||
flex: 1 1 auto; |
|||
display: flex; |
|||
flex-direction: column; |
|||
margin: 18px; |
|||
} |
|||
.miningOption{ |
|||
color: white; |
|||
/* display: flex; |
|||
flex: 1 1 auto; |
|||
flex-direction: row; |
|||
flex-wrap: wrap; */ |
|||
min-height: 215px; |
|||
justify-content: center; |
|||
align-items: center; |
|||
text-decoration: none; |
|||
} |
|||
|
|||
a.miningOption:hover{ |
|||
color: #f69b3a; |
|||
} |
|||
|
|||
.miningOption:first-child{ |
|||
background-color: #C99631; |
|||
} |
|||
.miningOption:last-child{ |
|||
background-color: #b064e1; |
|||
} |
|||
.miningOptionNum{ |
|||
font-size: 6em; |
|||
padding-right: 20px; |
|||
width: 140px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.gettingStartedMenu, |
|||
.gettingStartedMenu.pure-menu-open { |
|||
display: inline-block; |
|||
background-color: #887705; |
|||
width:30%; |
|||
} |
|||
|
|||
.pure-menu.pure-menu-open, .pure-menu.pure-menu-horizontal li .pure-menu-children { |
|||
border:none; |
|||
} |
|||
|
|||
.pure-menu .pure-menu-heading { |
|||
color: #FFF; |
|||
} |
|||
|
|||
.pure-menu li a:hover, |
|||
.pure-menu li a:focus { |
|||
background-color:#FCAD4C; |
|||
color:#1F152A; |
|||
} |
|||
|
|||
#orHolder{ |
|||
height: 37px; |
|||
text-align: center; |
|||
} |
|||
#orLine{ |
|||
border-bottom: 1px solid #c2cacf; |
|||
height: 19px; |
|||
margin-bottom: -13px; |
|||
} |
|||
#orText{ |
|||
background-color: #ebf4fa; |
|||
color: #5c5c5c; |
|||
display: inline-block; |
|||
width: 35px; |
|||
font-style: italic; |
|||
} |
|||
|
|||
#coinList code { |
|||
background-color:#1F152A; |
|||
} |
|||
|
|||
#coinInfoBackground{ |
|||
transition-duration: 400ms; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 0; |
|||
background-color: black; |
|||
opacity: 0.0; |
|||
} |
|||
#coinInfo{ |
|||
display: flex; |
|||
flex-direction: column; |
|||
color: white; |
|||
width: 750px; |
|||
min-height: 400px; |
|||
top: 50px; |
|||
left: 50%; |
|||
margin-left: -375px; |
|||
position: absolute; |
|||
background-color: #1F152A; |
|||
} |
|||
#coinInfo > div:first-of-type{ |
|||
font-size: 1.8em; |
|||
text-align: center; |
|||
margin-top: 40px; |
|||
margin-bottom: 35px; |
|||
} |
|||
.coinInfoHeader { |
|||
color: #FCAD4C; |
|||
font-weight:900; |
|||
font-family: 'Open Sans', sans-serif; |
|||
font-size:1.2em; |
|||
} |
|||
#coinInfoRows, |
|||
.coinInfoRows { |
|||
display: flex; |
|||
flex-direction: row; |
|||
justify-content: center; |
|||
flex: 1 1 auto; |
|||
margin-bottom: 70px; |
|||
} |
|||
code #coinInfoRows, |
|||
code .coinInfoRows { |
|||
margin-bottom:inherit; |
|||
} |
|||
#coinInfoRows > div, |
|||
.coinInfoRows > div { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
} |
|||
#coinInfoRows > div > div, |
|||
.coinInfoRows > div > div { |
|||
padding: 3px; |
|||
} |
|||
#coinInfoRowKeys, |
|||
.coinInfoRowKeys { |
|||
font-weight: bold; |
|||
padding-right: 30px; |
|||
} |
|||
#coinInfoRowKeys .coinInfoSubtle, |
|||
.coinInfoRowKeys .coinInfoSubtle { |
|||
font-weight: normal; |
|||
} |
|||
#coinInfoClose{ |
|||
position: absolute; |
|||
font-size: 3em; |
|||
top: 0; |
|||
right: 0; |
|||
width: 60px; |
|||
height: 60px; |
|||
text-align: center; |
|||
color: white; |
|||
text-decoration: none; |
|||
} |
|||
#coinInfoClose:hover{ |
|||
color: #50f0e3; |
|||
} |
|||
|
|||
/* STATS */ |
|||
#topCharts{ |
|||
color:#000; |
|||
padding: 18px; |
|||
} |
|||
#topCharts > div > div > svg{ |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.chartWrapper{ |
|||
background-color:#7e7e7e; |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.chartLabel{ |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
#boxesLower { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesLower > div { |
|||
display: flex; |
|||
} |
|||
#boxesLower > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
#boxStatsLeft{ |
|||
color: #FFF; |
|||
background-color: #1F152A; |
|||
} |
|||
#boxStatsRight{ |
|||
color: #FFF; |
|||
background-color: #1F152A; |
|||
} |
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
align-content: center; |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
|||
|
|||
div.tooltip { |
|||
/*position: absolute; */ |
|||
text-align: center; |
|||
padding: 2px 10px; |
|||
/* font: 12px sans-serif; */ |
|||
background: #000; |
|||
color: #EBEBEB; |
|||
border: 0px; |
|||
border-radius: 8px; |
|||
pointer-events: none; |
|||
} |
|||
#tooltip.hidden { |
|||
opacity: 0; |
|||
} |
|||
|
|||
/* TAB STATS */ |
|||
#topCharts { |
|||
padding: 18px; |
|||
} |
|||
|
|||
#topCharts > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
|
|||
.chartWrapper { |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
|
|||
.chartLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
/* WORKERS */ |
|||
#bottomNotes { |
|||
display: block; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-bottom: 18px; |
|||
} |
|||
#topPool > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.poolWrapper { |
|||
border: solid 1px #c7c7c7; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.poolLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
.poolMinerTable { |
|||
|
|||
} |
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
/* PAYMENTS */ |
|||
.paymentblocks { |
|||
max-width:475px; |
|||
word-wrap:break-word; |
|||
} |
|||
|
|||
#bottomNotes { |
|||
display: block; |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-bottom: 18px; |
|||
} |
|||
#topPool > div > div > svg { |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.poolLabel { |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
} |
|||
|
|||
/* WORKER STATS */ |
|||
#topCharts{ |
|||
padding-left: 18px; |
|||
padding-right: 18px; |
|||
padding-top: 18px; |
|||
padding-bottom: 0px; |
|||
} |
|||
#topCharts > div > div > svg{ |
|||
display: block; |
|||
height: 280px; |
|||
} |
|||
.chartWrapper{ |
|||
border: solid 1px #c7c7c7; |
|||
border-radius: 5px; |
|||
padding: 5px; |
|||
margin-bottom: 18px; |
|||
} |
|||
.chartLabel{ |
|||
font-size: 1.2em; |
|||
text-align: center; |
|||
padding: 4px; |
|||
} |
|||
.chartHolder{ |
|||
|
|||
} |
|||
|
|||
#boxesWorkers { |
|||
margin: 0 9px; |
|||
} |
|||
#boxesWorkers > div { |
|||
display: flex; |
|||
} |
|||
#boxesWorkers > div > div { |
|||
flex: 1 1 auto; |
|||
margin: 0 9px 18px 9px; |
|||
padding: 10px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
.boxLowerHeader{ |
|||
font-size: 1.3em; |
|||
margin: 0 0 5px 10px; |
|||
} |
|||
|
|||
.boxStats{ |
|||
color: white; |
|||
} |
|||
.boxStatsList{ |
|||
display: flex; |
|||
flex-flow: row wrap; |
|||
justify-content: space-around; |
|||
opacity: 0.77; |
|||
margin-bottom: 5px; |
|||
flex: 1 1 auto; |
|||
align-content: center; |
|||
} |
|||
.boxStatsList i.fa{ |
|||
height: 15px; |
|||
width: 33px; |
|||
text-align: center; |
|||
} |
|||
.boxStatsList > div{ |
|||
padding: 5px 20px; |
|||
} |
|||
.boxStatsList > div > div{ |
|||
padding: 3px; |
|||
} |
@ -0,0 +1,79 @@ |
|||
$(function() { |
|||
initStatData(); |
|||
|
|||
$('.btn-lg').click(function(){ |
|||
window.location = "workers/" + $('.input-lg').val(); |
|||
}); |
|||
|
|||
statsSource.addEventListener('message', function (e) { |
|||
if (document.querySelector('#pageWorkers') !== null) { |
|||
var stats = JSON.parse(e.data); |
|||
|
|||
for (var f = 0; f < poolKeys.length; f++) { |
|||
var pool = poolKeys[f]; |
|||
var sharesTotal = 0; |
|||
|
|||
for (var addr in stats.pools[pool].miners) { |
|||
var workerstat = stats.pools[pool].miners[addr]; |
|||
sharesTotal += parseFloat(workerstat.shares); |
|||
var existingRow = document.querySelector('#workers' + pool + ' #worker' + addr); |
|||
var minerEfficiency = ( workerstat.shares > 0 ) ? Math.floor(10000 * workerstat.shares / (workerstat.shares + workerstat.invalidshares)) / 100 : 0; |
|||
|
|||
if (existingRow == null) { |
|||
//Add new
|
|||
var insertMinerTr = document.createElement('tr'); |
|||
insertMinerTr.id = 'worker' + addr; |
|||
insertMinerTr.setAttribute('data-hashrate', workerstat.hashrate); |
|||
insertMinerTr.innerHTML = '<td><span class="responsiveTableLabel"><i class="far fa-address-card fa-fw"></i> Address: </span><a href="/workers/' + addr + '" title="' + addr + '">'+ middleEllipsis(addr, 20) + '</a></td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-cog fa-fw"></i> Shares: </span><span>' + bigNumber(workerstat.shares) + '</span></td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-bullseye fa-fw"></i> Efficiency: </span><span> ' + minerEfficiency + '%</span></td>'; |
|||
insertMinerTr.innerHTML += '<td><span class="responsiveTableLabel"><i class="fas fa-tachometer-alt fa-fw"></i> Hashrate: </span><span>' + workerstat.hashrateString + '</span></td>'; |
|||
|
|||
document.querySelector('#workers' + pool + ' .poolMinerTable tbody').appendChild(insertMinerTr); |
|||
|
|||
console.log('Added new miner! [' + addr + ']'); |
|||
} else { |
|||
//Update existing
|
|||
document.querySelector('#workers' + pool + ' #worker' + addr + ' td:nth-child(2) span:nth-child(2)').innerHTML = bigNumber(workerstat.shares); |
|||
document.querySelector('#workers' + pool + ' #worker' + addr + ' td:nth-child(3) span:nth-child(2)').innerHTML = minerEfficiency + '%'; |
|||
document.querySelector('#workers' + pool + ' #worker' + addr + ' td:nth-child(4) span:nth-child(2)').innerHTML = workerstat.hashrateString; |
|||
document.querySelector('#workers' + pool + ' #worker' + addr).setAttribute('data-hashrate', workerstat.hashrate); |
|||
} |
|||
} |
|||
|
|||
document.querySelector('#statsShares' + pool).innerHTML = bigNumber(sharesTotal); |
|||
|
|||
//Remove inactive
|
|||
var workerList = document.querySelectorAll('#workers' + pool + ' .poolMinerTable tbody tr'); |
|||
for ( var i = workerList.length-1; i >= 0; i--) { |
|||
var workerTrID = workerList[i].id.substring(6); |
|||
|
|||
if( typeof stats.pools[pool].miners[workerTrID] == 'undefined') { |
|||
console.log('Removing miner :( [' + workerTrID + ']'); |
|||
workerList[i].parentNode.removeChild(workerList[i]); |
|||
} |
|||
} |
|||
|
|||
//Resort table
|
|||
var table = document.querySelector('#workers' + pool + ' table.poolMinerTable tbody'); |
|||
var rows = document.querySelectorAll('#workers' + pool + ' table.poolMinerTable tbody tr'); |
|||
var rowsArr = [].slice.call(rows).sort(function (a, b) { |
|||
return (parseFloat(a.dataset.hashrate) == parseFloat(b.dataset.hashrate)) ? 0 : ((parseFloat(a.dataset.hashrate) < parseFloat(b.dataset.hashrate)) ? 1 : -1); |
|||
}); |
|||
for (var i = 0; i < rowsArr.length; i++){ table.append(rowsArr[i]); } |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
function searchKeyPress(e) |
|||
{ |
|||
// look for window.event in case event isn't passed in
|
|||
e = e || window.event; |
|||
if (e.keyCode == 13) |
|||
{ |
|||
document.getElementById('btnSearch').click(); |
|||
return false; |
|||
} |
|||
return true; |
|||
} |