Commit bbdd4b57 authored by Jason Rhinelander's avatar Jason Rhinelander
Browse files

Private pools; last update; minor page tweaks

parent 0c22662c
......@@ -98,7 +98,7 @@ def pool_stats(env, start_response, q):
mean_effort[x[0]] = (eff * 100, count, pval)
cur.execute("""
SELECT id, name, url, blocks_url, location, payment_system, warning,
SELECT id, name, url, blocks_url, location, payment_system, warning, private, last_update,
height, blocks_found, hashrate, miners, miners_paid, payments, fee, threshold, error,
hashrate_1d AS hr1, hashrate_7d AS hr7
FROM pools JOIN pool_stats ON pool = id JOIN pool_agg_stats ON pool_stats.pool = pool_agg_stats.pool
......@@ -117,6 +117,12 @@ def pool_stats(env, start_response, q):
del p['id']
pool_index[pool_id] = len(data['pools'])
if p['private']:
del p['fee']
if p['last_update']:
p['last_update'] = p['last_update'].timestamp()
# Look for out-of-sync pools; this can happen in two ways: a pool could have desynched (or
# not forked) and stalled, with height too low, or it could have desynched and run away,
# with height too high. On the other hand, it's normal to see pools 1-2 blocks out of sync
......@@ -177,6 +183,11 @@ def pool_list(env, start_response, q):
pools = []
for pool in cur:
p = dict(pool)
if p['private']:
del p['api_url']
del p['blocks_url']
if 'last_update' in p and p['last_update']:
p['last_update'] = p['last_update'].timestamp()
pools.append(p)
start_response('200 OK', [('Content-Type', 'application/json')])
......@@ -192,10 +203,10 @@ def find_pool(env, start_response, q):
if find:
pg = pgsql()
cur = pg.cursor()
cur.execute("SELECT name, blocks_url, height, hash FROM pool_blocks JOIN pools ON pool = id WHERE hash IN %s",
cur.execute("SELECT name, blocks_url, height, hash, private FROM pool_blocks JOIN pools ON pool = id WHERE hash IN %s",
(tuple(find),))
for row in cur:
blocks[row['hash']] = { 'pool': row['name'], 'height': row['height'], 'blocks_url': row['blocks_url'] }
blocks[row['hash']] = { 'pool': row['name'], 'height': row['height'], 'blocks_url': None if row['private'] else row['blocks_url'] }
for h in find:
if h not in blocks:
blocks[h] = None
......@@ -268,12 +279,16 @@ def add_pool(env, start_response, q):
pg = pgsql()
cur = pg.cursor()
try:
cur.execute("INSERT INTO pools (name, url, blocks_url, api, api_url, warning, enabled, location, payment_system) "
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s)",
(q['name'], q['url'], q['blocks_url'], q['api'], q['api_url'],
cur.execute("INSERT INTO pools (name, url, blocks_url, api, api_url, warning, enabled, location, payment_system, private) "
"VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)",
(q['name'], q['url'],
q['blocks_url'] if q['blocks_url'] else None,
q['api'], q['api_url'],
q['warning'] if q['warning'] else None,
bool('enabled' in q and q['enabled']),
q['location'], q['payment_system']))
q['location'], q['payment_system'],
bool('private' in q and q['private'])
))
except psycopg2.Error as e:
return error(start_response, "Unable to insert pool: " + str(e))
......@@ -290,13 +305,20 @@ def modify_pool(env, start_response, q):
update = []
params = []
private = bool('private' in q and q['private'])
for x in ('name', 'url', 'blocks_url', 'api', 'api_url', 'location', 'payment_system'):
if x in q:
if x in ('blocks_url', 'api_url') and not q[x]:
del q[x]
continue
update.append(x)
params.append(q[x])
if 'warning' in q:
update.append('warning')
params.append(q['warning'] if q['warning'] else None)
if 'private' in q:
update.append('private')
params.append(bool(q['private']))
if 'enabled' in q:
update.append('enabled')
params.append(bool(q['enabled']))
......
......@@ -182,11 +182,11 @@ def fetch_ncp(p):
result['hashrate'] = round(data['pool']['hashrate'])
result['blocks_found'] = data['pool']['totalBlocks']
result['miners'] = data['pool']['miners']
result['miners_paid'] = data['pool']['totalMinersPaid']
if 'totalMinersPaid' in data['pool']:
result['miners_paid'] = data['pool']['totalMinersPaid']
result['payments'] = data['pool']['totalPayments']
result['height'] = int(data['network']['height']) + (1 if p['api'].endswith('+1') else 0)
result['difficulty'] = int(data['network']['difficulty'])
result['effort'] = data['pool']['roundHashes'] / float(result['difficulty']) * 100
global fetch_all_blocks
if any(x in fetch_all_blocks for x in ('all', int(p['id']), p['name'])):
......@@ -233,7 +233,6 @@ def fetch_nodejs(p):
result['payments'] = pool['totalPayments']
result['height'] = int(network['height']) + (1 if p['api'].endswith('+1') else 0)
result['difficulty'] = int(network['difficulty'])
result['effort'] = pool['roundHashes'] / float(result['difficulty']) * 100
return result
......@@ -307,13 +306,14 @@ for p in results['pools']:
if 'error' in r:
cur.execute('INSERT INTO pool_stats (pool_fetch, pool, error) VALUES (%s, %s, %s)', (fetch_id, poolid, r['error']))
else:
for x in ('height', 'blocks_found', 'hashrate', 'effort', 'miners', 'miners_paid', 'payments', 'fee', 'threshold'):
for x in ('height', 'blocks_found', 'hashrate', 'miners', 'miners_paid', 'payments', 'fee', 'threshold'):
if x not in r:
r[x] = None
cur.execute('INSERT INTO pool_stats (pool_fetch, pool, height, blocks_found, hashrate, effort, miners, miners_paid, payments, fee, threshold) ' +
'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
(fetch_id, poolid, r['height'], r['blocks_found'], r['hashrate'], r['effort'], r['miners'], r['miners_paid'], r['payments'], r['fee'], r['threshold'])
cur.execute('INSERT INTO pool_stats (pool_fetch, pool, height, blocks_found, hashrate, miners, miners_paid, payments, fee, threshold) ' +
'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
(fetch_id, poolid, r['height'], r['blocks_found'], r['hashrate'], r['miners'], r['miners_paid'], r['payments'], r['fee'], r['threshold'])
)
cur.execute('UPDATE pools SET last_update = NOW() WHERE id = %s', (poolid,))
for b in r['blocks']:
cur.execute('INSERT INTO pool_blocks (pool, height, hash, effort) VALUES (%s, %s, %s, %s) ON CONFLICT DO NOTHING',
......
......@@ -161,6 +161,16 @@
</div>
</div>
<div class="pool field">
<label>Private?</label>
<div class="pool input">
<select id="pool-private"><option value="">Public</option><option value="1">Private</option></select>
</div>
<div class="description">
If private this pool's API URL, blocks URL, and pool fee will be hidden.
</div>
</div>
<div class="pool field">
<label>Enabled?</label>
<div class="pool input">
......
......@@ -47,7 +47,6 @@ $(function() {
var set_system = function(x) { return function() { $('#pool-payment_system').val(x); return false; }; };
for (let x of ['Score', 'Prop', 'PPLNS', 'PPLNT']) {
console.log(x.toLowerCase());
$('a#payments_' + x.toLowerCase()).off('click').on('click', set_system(x));
}
});
......@@ -116,7 +115,9 @@ function populate_pool_form() {
for (let x of ['name', 'location', 'payment_system', 'url', 'blocks_url', 'api', 'api_url', 'warning']) {
$('#pool-' + x).val(pool[x]);
}
$('#pool-enabled').val(pool.enabled ? '1' : '');
for (let x of ['private', 'enabled']) {
$('#pool-' + x).val(pool[x] ? '1' : '');
}
$('#pool-submit').text('Modify ' + pool.name);
}
}
......@@ -139,7 +140,7 @@ function submit_pool() {
auth_code: $('#auth_code').val()
};
if (id) data['id'] = id;
for (let x of ['name', 'url', 'location', 'payment_system', 'blocks_url', 'api', 'api_url', 'warning', 'enabled'])
for (let x of ['name', 'url', 'location', 'payment_system', 'blocks_url', 'api', 'api_url', 'warning', 'private', 'enabled'])
data[x] = $('#pool-' + x).val();
$.ajax({
......
......@@ -10,11 +10,12 @@
<title>Graft pool list</title>
<script src="/javascript/jquery/jquery.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="pools.js?7"></script>
<script src="pools.js?8"></script>
<link rel="stylesheet" href="pools.css?7" />
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="stylesheet" href="/javascript/jquery-ui/themes/base/jquery-ui.css" />
<script src="/javascript/jquery-ui/jquery-ui.min.js"></script>
<script src="/javascript/jquery-timeago/jquery.timeago.min.js"></script>
</head>
<body>
<div class="top-stats">
......@@ -26,7 +27,9 @@
<div class="diff" title="This is difficulty required to have an average block of two minutes when the actual speed is the Network speed, above"><label>Difficulty:</label> <div id="difficulty">Loading...</div></div>
<div class="height" title="The current block height that the network is trying to solve, i.e. the largest height found plus one"><label>Height:</label> <div id="height">Loading...</div></div>
<div class="pools_speed" title="This is the sum of hash rates of all pools on this list, excluding desynchronized pools"><label class="long">Pools Speed:</label><label class="short">Pools Sp.:</label> <div id="pools-speed">Loading...</div></div>
<div class="block_exp"><label>Block Explorer:</label> <div>
<div class="block_exp"><label>Block Explorer:</label>
<div>
<a href="https://graft.observer">Graft.Observer</a>
<a href="https://blockexplorer.graft.network">Official</a>
</div></div>
</div>
......
......@@ -163,7 +163,10 @@ function update_pools(data) {
if (pool.error) {
var failure = $(' <span class="failure">✘</span>');
failure.attr("title", "Failed to fetch " + pool.name + " data: " + pool.error);
var last_upd = '(Unknown)';
if (pool.last_update)
last_upd = $.timeago(new Date(pool.last_update * 1000));
failure.attr("title", "Failed to fetch " + pool.name + " data: " + pool.error + '\n\nLast successful update: ' + last_upd);
row.find('td.name').append(failure);
}
else {
......@@ -188,11 +191,13 @@ function update_pools(data) {
let effort_info = 'Since block ' + eff.since + ' (~' + ((data.height - eff.since) / target_blocks_per_day) + ' days ago) ' + pool.dispname + ' has found ' + eff.blocks + ' blocks ' +
'with an average effort over those blocks of ' + eff.mean.toPrecision(4) + '%.';
if (eff.pval < .05) {
effort_info += '<br /><br />Statistically, the probability of a fair pool with this many blocks being this ' + (eff.mean > 100 ? 'unlucky' : 'lucky') + ' equals ' +
effort_info += '<br /><br />Statistically, the probability of a fair pool with this many blocks being this ' + (eff.mean > 100 ? 'unlucky' : 'lucky') + ' on an ideal network equals ' +
eff.pval.toPrecision(4).replace(/e(-\d+)/, '×10<sup>$1</sup>') + '.';
}
if (eff.pval < .0001) {
let badEffort = eff.pval < .0001;
badEffort = false; // disable for now
if (badEffort) {
if (eff.mean > 100) {
effort_info += '<br /><br />Since it is so statistically unlikely that this high average effort is simply due to bad luck, we\'ve added ' +
'the effective loss due to the abnormally high effort value as an effective fee in addition to this ' +
......@@ -216,7 +221,7 @@ function update_pools(data) {
tde.append(stars);
}
if (eff.pval < .0001 && eff.mean > 100) {
if (badEffort && eff.mean > 100) {
let tdfee = row.find('td.fee');
let fee = parseFloat(tdfee.text());
fee += 100.0 * (1.0 - 100.0/eff.mean)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment