Add masternodes page/feature

-Added a new "Masternodes" page which displays the current list of masternodes on the network
-/api/getmasternodelist is no longer publicly accessible and has been replaced by /ext/getmasternodelist which returns the masternode list from local collection instead of directly from wallet
-Added new masternode sync options to sync.js and sync.sh
-Added new masternodes_last_updated field to the Stats collection
-Updated delete_database.sh and restore_backup.sh to include support for the new masternodes collection
-Network header menu icon changed to allow the new Masternodes menu item to use the old network icon
This commit is contained in:
joeuhren
2020-12-30 18:22:02 -07:00
parent d787a03fe2
commit be1205d90a
17 changed files with 494 additions and 62 deletions
+3 -1
View File
@@ -115,6 +115,7 @@ sync.sh (located in scripts/) is used for updating the local databases. This scr
reindex-txcount Rescan and flatten the tx count value for faster access
market Updates market summaries, orderbooks, trade history + charts
peers Updates peer info based on local wallet connections
masternodes Updates the list of active masternodes on the network
Notes:
- 'current block' is the latest created block when script is executed.
@@ -126,11 +127,12 @@ sync.sh (located in scripts/) is used for updating the local databases. This scr
**crontab**
*Example crontab; update index every minute, market data every 2 minutes and peers every 5 minutes*
*Example crontab; update index every minute, market data every 2 minutes, peers and masternodes every 5 minutes*
*/1 * * * * /path/to/explorer/scripts/sync.sh /path/to/nodejs update > /dev/null 2>&1
*/2 * * * * /path/to/explorer/scripts/sync.sh /path/to/nodejs market > /dev/null 2>&1
*/5 * * * * /path/to/explorer/scripts/sync.sh /path/to/nodejs peers > /dev/null 2>&1
*/5 * * * * /path/to/explorer/scripts/sync.sh /path/to/nodejs masternodes > /dev/null 2>&1
### Wallet
+22 -1
View File
@@ -346,6 +346,24 @@ app.use('/ext/connections', function(req,res){
});
});
// get the list of masternodes from local collection
app.use('/ext/getmasternodelist', function(req, res) {
// check if the getmasternodelist api is enabled or else check the headers to see if it matches an internal ajax request from the explorer itself (TODO: come up with a more secure method of whitelisting ajax calls from the explorer)
if (settings.public_api.ext['getmasternodelist'] || (req.headers['x-requested-with'] != null && req.headers['x-requested-with'].toLowerCase() == 'xmlhttprequest' && req.headers.referer != null && req.headers.accept.indexOf('text/javascript') > -1 && req.headers.accept.indexOf('application/json') > -1)) {
// get the masternode list from local collection
db.get_masternodes(function(masternodes) {
// loop through masternode list and remove the mongo _id and __v keys
for (i = 0; i < masternodes.length; i++) {
delete masternodes[i]['_doc']['_id'];
delete masternodes[i]['_doc']['__v'];
}
// return masternode list
res.send(masternodes);
});
} else
res.end('This method is disabled');
});
// locals
app.set('title', settings.title);
app.set('explorer_version', package_metadata.version);
@@ -390,9 +408,12 @@ app.set('labels', settings.labels);
app.set('homelink', settings.homelink);
app.set('logoheight', settings.logoheight);
app.set('burned_coins', settings.burned_coins);
app.set('public_api', settings.public_api);
app.set('api_cmds', settings.api_cmds);
// Always disable the rpc masternode list cmd from public apis
settings.public_api['rpc']['getmasternodelist'] = false;
app.set('public_api', settings.public_api);
app.set('sticky_header', settings.sticky_header);
app.set('sticky_footer', settings.sticky_footer);
+182 -6
View File
@@ -1,6 +1,7 @@
var mongoose = require('mongoose')
, Stats = require('../models/stats')
, Markets = require('../models/markets')
, Masternode = require('../models/masternode')
, Address = require('../models/address')
, AddressTx = require('../models/addresstx')
, Tx = require('../models/tx')
@@ -250,12 +251,30 @@ module.exports = {
return fs.existsSync('./tmp/show_sync_message.tmp');
},
update_label: function(hash, message, cb) {
update_label: function(hash, claim_name, cb) {
find_address(hash, false, function(address) {
if (address) {
Address.updateOne({a_id: hash}, {
name: message
name: claim_name
}, function() {
// update claim name in richlist
module.exports.update_richlist_claim_name(hash, claim_name, function() {
// update claim name in masternode list
module.exports.update_masternode_claim_name(hash, claim_name, function() {
return cb('');
});
});
});
} else {
// address is not valid or does not have any transactions
return cb('no_address');
}
});
},
update_richlist_claim_name: function(hash, claim_name, cb) {
// check if the richlist is enabled
if (settings.display.richlist) {
// ensure that if this address exists in the richlist that it displays the new alias
module.exports.get_richlist(settings.coin, function(richlist) {
var updated = false;
@@ -264,7 +283,7 @@ module.exports = {
// check if this is the correct address
if (richlist.received[r].a_id == hash) {
// update the claim name
richlist.received[r]['name'] = message;
richlist.received[r]['name'] = claim_name;
// mark as updated
updated = true;
}
@@ -274,7 +293,7 @@ module.exports = {
// check if this is the correct address
if (richlist.balance[b].a_id == hash) {
// update the claim name
richlist.balance[b]['name'] = message;
richlist.balance[b]['name'] = claim_name;
// mark as updated
updated = true;
}
@@ -294,12 +313,46 @@ module.exports = {
return cb('');
}
});
} else {
// richlist is not enabled so nothing to update
return cb('');
}
},
update_masternode_claim_name: function(hash, claim_name, cb) {
// check if the masternode list is enabled
if (settings.display.masternodes) {
// ensure that if this address exists in the masternode that it displays the new alias
module.exports.get_masternodes(function(masternodes) {
var updated = false;
// loop through masternode addresses
for (m = 0; m < masternodes.length; m++) {
// check if this is the correct address
if (masternodes[m].addr == hash) {
// update the claim name
masternodes[m]['claim_name'] = claim_name;
// mark as updated
updated = true;
}
}
// check if the address was updated in the masternode list
if (updated) {
// save the updated masternode back to collection
Masternode.updateOne({addr: hash}, {
claim_name: claim_name
}, function() {
// finished updating the claim label
return cb('');
});
} else {
// address is not valid or does not have any transactions
return cb('no_address');
// finished updating the claim label
return cb('');
}
});
} else {
// masternode list is not enabled so nothing to update
return cb('');
}
},
check_stats: function(coin, cb) {
@@ -993,6 +1046,129 @@ module.exports = {
});
},
// determine if masternode exists and save masternode to collection
save_masternode: function (raw_masternode, cb) {
// lookup masternode in local collection
module.exports.find_masternode(raw_masternode.txhash, raw_masternode.outidx, function (masternode) {
// determine if the claim address feature is enabled
if (settings.display.claim_address) {
// claim address is enabled so lookup the address claim name
find_address(raw_masternode.addr, false, function(address) {
if (address) {
// save claim name to masternode obejct
raw_masternode.claim_name = address.name;
} else {
// save blank claim name to masternode obejct
raw_masternode.claim_name = '';
}
// add/update the masternode
module.exports.add_update_masternode(raw_masternode, (masternode == null), function(success) {
return cb(success);
});
});
} else {
// claim address is disabled so add/update the masternode
module.exports.add_update_masternode(raw_masternode, (masternode == null), function(success) {
return cb(success);
});
}
});
},
// add or update a single masternode
add_update_masternode(masternode, add, cb) {
if (!masternode.txhash == null || !masternode.outidx == null) {
console.log('Masternode Update - TX or Outidx is missing');
console.log(masternode.txhash);
console.log(masternode.outidx);
return cb(false);
} else {
var mn = new Masternode({
rank: masternode.rank,
network: masternode.network,
txhash: masternode.txhash,
outidx: masternode.outidx,
status: masternode.status,
addr: masternode.addr,
version: masternode.version,
lastseen: masternode.lastseen,
activetime: masternode.activetime,
lastpaid: masternode.lastpaid,
claim_name: (masternode.claim_name == null ? '' : masternode.claim_name)
});
if (add) {
// add new masternode to collection
mn.save(function (err) {
if (err) {
console.log(err);
return cb(false);
} else
return cb(true);
});
} else {
// update existing masternode in local collection
Masternode.updateOne({ txhash: masternode.txhash, outidx: masternode.outidx }, masternode, function (err) {
if (err) {
console.log(err);
return cb(false);
} else
return cb(true);
});
}
}
},
// find masternode by txid and offset
find_masternode: function (txhash, outidx, cb) {
Masternode.findOne({ txhash: txhash, outidx: outidx }, function (err, masternode) {
if (err)
return cb(null);
else {
if (masternode)
return cb(masternode);
else
return cb(null);
}
});
},
// remove masternodes older than 24 hours
remove_old_masternodes: function (cb) {
Masternode.deleteMany({ lastseen: { $lte: (Math.floor(Date.now() / 1000) - 86400) } }, function (err) {
if (err) {
console.log(err);
return cb();
} else
return cb();
});
},
// get the list of masternodes from local collection
get_masternodes: function (cb) {
Masternode.find({}, function (err, masternodes) {
if (err)
return cb([]);
else
return cb(masternodes);
});
},
// updates last_updated stats; called by sync.js
update_last_updated_stats: function (coin, param, cb) {
if (param.masternodes_last_updated) {
// update masternode last updated date
Stats.updateOne({ coin: coin }, {
masternodes_last_updated: param.masternodes_last_updated
}, function () {
return cb(true);
});
} else {
// invalid option
return cb(false);
}
},
populate_claim_address_names: function(tx, cb) {
var addresses = [];
+28
View File
@@ -248,6 +248,34 @@ module.exports = {
}
},
get_masternodelist: function(cb) {
var cmd = prepareRpcCommand(settings.api_cmds.getmasternodelist);
if (!(cmd.method == '' && cmd.parameters.length == 0)) {
if (settings.use_rpc) {
rpcCommand([{method:cmd.method, parameters: cmd.parameters}], function(response) {
// check if an error msg was received from the rpc server
if (response == 'There was an error. Check your console.')
return cb(null);
else
return cb(response);
});
} else {
var uri = base_url + 'getmasternodelist';
request({uri: uri, json: true, headers: {'User-Agent': 'eiquidus'}}, function (error, response, body) {
// check if an error msg was received from the web api server
if (body == 'There was an error. Check your console.')
return cb(null);
else
return cb(body);
});
}
} else {
// cmd not in use. return null.
return cb(null);
}
},
get_masternodecount: function(cb) {
var cmd = prepareRpcCommand(settings.api_cmds.getmasternodecount);
+3 -2
View File
@@ -92,6 +92,7 @@ exports.display = {
"richlist": true,
"movement": true,
"network": true,
"masternodes": true,
"claim_address": true,
"claim_address_header_menu": true,
"page_header_bgcolor": "",
@@ -201,7 +202,6 @@ exports.public_api = {
"getnetworkhashps": true,
"getvotelist": true,
"getmasternodecount": true,
"getmasternodelist": true,
"getmaxmoney": true,
"getmaxvote": true,
"getvote": true,
@@ -219,7 +219,8 @@ exports.public_api = {
"getbalance": true,
"getlasttxs": true,
"getcurrentprice": true,
"getbasicstats": true
"getbasicstats": true,
"getmasternodelist": true
}
};
+18
View File
@@ -0,0 +1,18 @@
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var MasternodeSchema = new Schema({
rank: { type: Number, default: 0 },
network: { type: String, default: "" },
txhash: { type: String, default: "" },
outidx : { type: Number, default: 0},
status : { type: String, default: "" },
addr: { type: String, unique: true, index: true },
version : { type: Number, default: 0},
lastseen: { type: Number, default: 0 },
activetime: { type: Number, default: 0 },
lastpaid: { type: Number, default: 0 },
claim_name: { type: String, default: '', index: true }
}, {id: false});
module.exports = mongoose.model('Masternode', MasternodeSchema);
+1 -2
View File
@@ -5,13 +5,12 @@ var StatsSchema = new Schema({
coin: { type: String },
count: { type: Number, default: 1 },
last: { type: Number, default: 1 },
//difficulty: { type: Object, default: {} },
//hashrate: { type: String, default: 'N/A' },
supply: { type: Number, default: 0 },
txes: { type: Number, default: 0 },
connections: { type: Number, default: 0 },
last_price: { type: Number, default: 0 },
last_usd_price: { type: Number, default: 0 },
masternodes_last_updated: { type: Number, default: 0 }
});
module.exports = mongoose.model('coinstats', StatsSchema);
+1 -1
View File
@@ -465,7 +465,7 @@ table {
padding-top: 15px !important;
}
.dataTables_length {
.dataTables_length, .dataTables_filter {
padding-top: 10px;
}
+14
View File
@@ -255,6 +255,20 @@ router.get('/network', function(req, res) {
res.render('network', {active: 'network', showSync: db.check_show_sync_message()});
});
// masternode list page
router.get('/masternodes', function(req, res) {
// ensure masternode page is enabled
if (settings.display.masternodes == true) {
// lookup last updated date
db.get_stats(settings.coin, function (stats) {
res.render('masternodes', {active: 'masternodes', last_updated: stats.masternodes_last_updated, showSync: db.check_show_sync_message()});
});
} else {
// masternode page is not enabled so default to the index page
route_get_index(res, null);
}
});
router.get('/reward', function(req, res) {
if (settings.heavy) {
db.get_stats(settings.coin, function (stats) {
+2
View File
@@ -29,6 +29,8 @@ db.heavies.remove({})
db.heavies.drop()
db.markets.remove({})
db.markets.drop()
db.masternodes.remove({})
db.masternodes.drop()
db.peers.remove({})
db.peers.drop()
db.richlists.remove({})
+2
View File
@@ -42,6 +42,8 @@ db.heavies.remove({})
db.heavies.drop()
db.markets.remove({})
db.markets.drop()
db.masternodes.remove({})
db.masternodes.drop()
db.peers.remove({})
db.peers.drop()
db.richlists.remove({})
+39
View File
@@ -23,6 +23,7 @@ function usage() {
console.log('reindex-txcount Rescan and flatten the tx count value for faster access');
console.log('market Updates market summaries, orderbooks, trade history + charts');
console.log('peers Updates peer info based on local wallet connections');
console.log('masternodes Updates the list of active masternodes on the network');
console.log('');
console.log('Notes:');
console.log('- \'current block\' is the latest created block when script is executed.');
@@ -63,6 +64,8 @@ if (process.argv[2] == 'index') {
database = 'market';
} else if (process.argv[2] == 'peers') {
database = 'peers';
} else if (process.argv[2] == 'masternodes') {
database = 'masternodes';
} else {
usage();
}
@@ -236,6 +239,42 @@ if (database == 'peers') {
});
}
});
} else if (database == 'masternodes') {
console.log('syncing masternodes.. please wait..');
// syncing masternodes does not require a lock
mongoose.connect(dbString, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: false }, function(err) {
if (err) {
console.log('Unable to connect to database: %s', dbString);
console.log('Aborting');
exit();
} else {
lib.get_masternodelist(function (body) {
if (body != null) {
lib.syncLoop(body.length, function (loop) {
var i = loop.iteration();
db.save_masternode(body[i], function (success) {
if (success)
loop.next();
else {
console.log('error: cannot save masternode %s.', (body[i].addr ? body[i].addr : 'UNKNOWN'));
exit();
}
});
}, function () {
db.remove_old_masternodes(function (cb) {
db.update_last_updated_stats(settings.coin, { masternodes_last_updated: Math.floor(new Date() / 1000) }, function (cb) {
console.log('masternode sync complete');
exit();
});
});
});
} else {
console.log('no masternodes found');
exit();
}
});
}
});
} else {
// index and market sync requires locking
is_locked(function (exists) {
+9 -1
View File
@@ -34,6 +34,10 @@ if [ -n "${1}" ]; then
# Peers update
MODE="peers"
;;
"masternodes")
# Masternodes update
MODE="masternodes"
;;
*)
# Check if this is a file that exists on the filesystem
if [ -f ${1} ]; then
@@ -76,6 +80,10 @@ if [ -n "${1}" ]; then
# Peers update
MODE="peers"
;;
"masternodes")
# Masternodes update
MODE="masternodes"
;;
esac
elif [ -n "${NODE_PATH}" ]; then
# Node path was specified but no mode, so default to 'index update' mode
@@ -90,7 +98,7 @@ fi
if [ -n "${MODE}" ]; then
# Mode is set
# Check if the desired mode requires a lock
if [ "${MODE}" != "peers" ]; then
if [ "${MODE}" != "peers" ] && [ "${MODE}" != "masternodes" ]; then
# A lock is required
# Check if the script is already running (tmp/index.pid file already exists)
if [ -f "${EXPLORER_PATH}/tmp/index.pid" ]; then
+4 -2
View File
@@ -115,6 +115,8 @@
"search": true,
"movement": true,
"network": true,
// Show/hide the "Masternodes" header menu item
"masternodes": true,
// Enable/disable the ability for users to claim a wallet address. NOTE: Disabling this feature after addresses have already been claimed will effectively hide the claimed values and restore the original wallet addresses again
"claim_address": true,
// Show/hide the "Claim Address" header menu item. NOTE: "claim_address" setting must also be enabled or else the header item will automatically be hidden as well
@@ -285,7 +287,6 @@
"getnetworkhashps": true,
"getvotelist": true,
"getmasternodecount": true,
"getmasternodelist": true,
"getmaxmoney": true,
"getmaxvote": true,
"getvote": true,
@@ -303,7 +304,8 @@
"getbalance": true,
"getlasttxs": true,
"getcurrentprice": true,
"getbasicstats": true
"getbasicstats": true,
"getmasternodelist": true
}
},
+9 -9
View File
@@ -10,8 +10,8 @@ block content
p
em #{settings.locale.api_message}
hr
- var hide_rpc_api_section = !(settings.public_api.rpc['getdifficulty'] == true && settings.api_cmds['getdifficulty'] != null && settings.api_cmds['getdifficulty'] != '') && !(settings.public_api.rpc['getconnectioncount'] == true && settings.api_cmds['getconnectioncount'] != null && settings.api_cmds['getconnectioncount'] != '') && !(settings.public_api.rpc['getblockcount'] == true && settings.api_cmds['getblockcount'] != null && settings.api_cmds['getblockcount'] != '') && !(settings.public_api.rpc['getblockhash'] == true && settings.api_cmds['getblockhash'] != null && settings.api_cmds['getblockhash'] != '') && !(settings.public_api.rpc['getblock'] == true && settings.api_cmds['getblock'] != null && settings.api_cmds['getblock'] != '') && !(settings.public_api.rpc['getrawtransaction'] == true && settings.api_cmds['getrawtransaction'] != null && settings.api_cmds['getrawtransaction'] != '') && !(settings.public_api.rpc['getnetworkhashps'] == true && settings.index.show_hashrate == true && settings.api_cmds['getnetworkhashps'] != null && settings.api_cmds['getnetworkhashps'] != '') && !(settings.public_api.rpc['getvotelist'] == true && settings.api_cmds['getvotelist'] != null && settings.api_cmds['getvotelist'] != '') && !(settings.public_api.rpc['getmasternodecount'] == true && settings.api_cmds['getmasternodecount'] != null && settings.api_cmds['getmasternodecount'] != '') && !(settings.public_api.rpc['getmasternodelist'] == true && settings.api_cmds['getmasternodelist'] != null && settings.api_cmds['getmasternodelist'] != '') && (!settings.heavy || (!(settings.public_api.rpc['getmaxmoney'] == true && settings.api_cmds.heavies['getmaxmoney'] != null && settings.api_cmds.heavies['getmaxmoney'] != '') && !(settings.public_api.rpc['getmaxvote'] == true && settings.api_cmds.heavies['getmaxvote'] != null && settings.api_cmds.heavies['getmaxvote'] != '') && !(settings.public_api.rpc['getvote'] == true && settings.api_cmds.heavies['getvote'] != null && settings.api_cmds.heavies['getvote'] != '') && !(settings.public_api.rpc['getphase'] == true && settings.api_cmds.heavies['getphase'] != null && settings.api_cmds.heavies['getphase'] != '') && !(settings.public_api.rpc['getreward'] == true && settings.api_cmds.heavies['getreward'] != null && settings.api_cmds.heavies['getreward'] != '') && !(settings.public_api.rpc['getsupply'] == true && settings.api_cmds.heavies['getsupply'] != null && settings.api_cmds.heavies['getsupply'] != '') && !(settings.public_api.rpc['getnextrewardestimate'] == true && settings.api_cmds.heavies['getnextrewardestimate'] != null && settings.api_cmds.heavies['getnextrewardestimate'] != '') && !(settings.public_api.rpc['getnextrewardwhenstr'] == true && settings.api_cmds.heavies['getnextrewardwhenstr'] != null && settings.api_cmds.heavies['getnextrewardwhenstr'] != '')));
- var hide_ext_api_section = !settings.public_api.ext['getmoneysupply'] && !settings.public_api.ext['getdistribution'] && !settings.public_api.ext['getaddress'] && !settings.public_api.ext['gettx'] && !settings.public_api.ext['getbalance'] && !settings.public_api.ext['getlasttxs'] && !settings.public_api.ext['getcurrentprice'] && !settings.public_api.ext['getbasicstats'];
- var hide_rpc_api_section = !(settings.public_api.rpc['getdifficulty'] == true && settings.api_cmds['getdifficulty'] != null && settings.api_cmds['getdifficulty'] != '') && !(settings.public_api.rpc['getconnectioncount'] == true && settings.api_cmds['getconnectioncount'] != null && settings.api_cmds['getconnectioncount'] != '') && !(settings.public_api.rpc['getblockcount'] == true && settings.api_cmds['getblockcount'] != null && settings.api_cmds['getblockcount'] != '') && !(settings.public_api.rpc['getblockhash'] == true && settings.api_cmds['getblockhash'] != null && settings.api_cmds['getblockhash'] != '') && !(settings.public_api.rpc['getblock'] == true && settings.api_cmds['getblock'] != null && settings.api_cmds['getblock'] != '') && !(settings.public_api.rpc['getrawtransaction'] == true && settings.api_cmds['getrawtransaction'] != null && settings.api_cmds['getrawtransaction'] != '') && !(settings.public_api.rpc['getnetworkhashps'] == true && settings.index.show_hashrate == true && settings.api_cmds['getnetworkhashps'] != null && settings.api_cmds['getnetworkhashps'] != '') && !(settings.public_api.rpc['getvotelist'] == true && settings.api_cmds['getvotelist'] != null && settings.api_cmds['getvotelist'] != '') && !(settings.public_api.rpc['getmasternodecount'] == true && settings.api_cmds['getmasternodecount'] != null && settings.api_cmds['getmasternodecount'] != '') && (!settings.heavy || (!(settings.public_api.rpc['getmaxmoney'] == true && settings.api_cmds.heavies['getmaxmoney'] != null && settings.api_cmds.heavies['getmaxmoney'] != '') && !(settings.public_api.rpc['getmaxvote'] == true && settings.api_cmds.heavies['getmaxvote'] != null && settings.api_cmds.heavies['getmaxvote'] != '') && !(settings.public_api.rpc['getvote'] == true && settings.api_cmds.heavies['getvote'] != null && settings.api_cmds.heavies['getvote'] != '') && !(settings.public_api.rpc['getphase'] == true && settings.api_cmds.heavies['getphase'] != null && settings.api_cmds.heavies['getphase'] != '') && !(settings.public_api.rpc['getreward'] == true && settings.api_cmds.heavies['getreward'] != null && settings.api_cmds.heavies['getreward'] != '') && !(settings.public_api.rpc['getsupply'] == true && settings.api_cmds.heavies['getsupply'] != null && settings.api_cmds.heavies['getsupply'] != '') && !(settings.public_api.rpc['getnextrewardestimate'] == true && settings.api_cmds.heavies['getnextrewardestimate'] != null && settings.api_cmds.heavies['getnextrewardestimate'] != '') && !(settings.public_api.rpc['getnextrewardwhenstr'] == true && settings.api_cmds.heavies['getnextrewardwhenstr'] != null && settings.api_cmds.heavies['getnextrewardwhenstr'] != '')));
- var hide_ext_api_section = !settings.public_api.ext['getmoneysupply'] && !settings.public_api.ext['getdistribution'] && !settings.public_api.ext['getaddress'] && !settings.public_api.ext['gettx'] && !settings.public_api.ext['getbalance'] && !settings.public_api.ext['getlasttxs'] && !settings.public_api.ext['getcurrentprice'] && !settings.public_api.ext['getbasicstats'] && !(settings.public_api.ext['getmasternodelist'] == true && settings.api_cmds['getmasternodelist'] != null && settings.api_cmds['getmasternodelist'] != '');
if !hide_rpc_api_section
h3 #{settings.locale.api_calls}
p
@@ -83,13 +83,6 @@ block content
div
em #{settings.locale.api_getmasternodecount}
a(href='/api/getmasternodecount') #{address}/api/getmasternodecount
if settings.public_api.rpc['getmasternodelist'] == true && settings.api_cmds['getmasternodelist'] != null && settings.api_cmds['getmasternodelist'] != ''
li
p
div.font-weight-bold getmasternodelist
div
em #{settings.locale.api_getmasternodelist}
a(href='/api/getmasternodelist') #{address}/api/getmasternodelist
if settings.heavy == true
if settings.public_api.rpc['getmaxmoney'] == true && settings.api_cmds.heavies['getmaxmoney'] != null && settings.api_cmds.heavies['getmaxmoney'] != ''
li
@@ -212,6 +205,13 @@ block content
div
em Returns basic statistics about the coin including: block count, circulating supply, USD price, BTC price and # of masternodes
a(href='/ext/getbasicstats') #{address}/ext/getbasicstats
if settings.public_api.ext['getmasternodelist'] == true && settings.api_cmds['getmasternodelist'] != null && settings.api_cmds['getmasternodelist'] != ''
li
p
div.font-weight-bold getmasternodelist
div
em #{settings.locale.api_getmasternodelist}
a(href='/ext/getmasternodelist') #{address}/ext/getmasternodelist
hr
h3 Linking (GET)
p
+6 -1
View File
@@ -324,6 +324,11 @@ html(lang='en')
a.nav-link(href='/reward')
span.fa.fa-star
span.margin-left-5 #{settings.locale.menu_reward}
if settings.display.masternodes == true
li#masternodes
a.nav-link(href='/masternodes')
span.fa.fa-share-alt
span.margin-left-5 Masternodes
if settings.display.movement == true
li#movement
a.nav-link.loading(href='/movement')
@@ -332,7 +337,7 @@ html(lang='en')
if settings.display.network == true
li#network
a.nav-link(href='/network')
span.fa.fa-share-alt
span.fas.fa-network-wired
span.margin-left-5 #{settings.locale.menu_network}
if settings.display.richlist == true
li#richlist
+115
View File
@@ -0,0 +1,115 @@
extends layout
block content
include ./includes/common.pug
script.
function secondsToHms(d) {
d = Number(d);
var h = Math.floor(d / 3600);
var m = Math.floor(d % 3600 / 60);
var s = Math.floor(d % 3600 % 60);
var dy = Math.floor(h / 24);
var h = h % 24;
return ('0' + dy).slice(-2) + " day" + (('0' + dy).slice(-2) == 1 ? "" : "s") + " " + ('0' + h).slice(-2) + "h " + ('0' + m).slice(-2) + "m " + ('0' + s).slice(-2) + "s";
}
$(document).ready(function() {
var labels = !{JSON.stringify(settings.labels)};
var ctable = $('#masternodes-table').dataTable({
autoWidth: true,
searching: true,
ordering: true,
order: [[ 5, 'desc' ]],
responsive: true,
lengthChange: true,
processing: true,
scrollX: true,
language: {
paginate: {
previous: '<',
next: '>'
}
},
ajax: {
url: '/ext/getmasternodelist',
dataSrc: function(json) {
for (i = 0; i < json.length; i++) {
var addr = json[i]['addr'];
json[i]['address'] = "<a href='/address/" + json[i]['address'] + "'>" + json[i]['address'] + "</a>";
json[i]['lastseen'] = new Date((json[i]['lastseen']) * 1000).toLocaleString();
if (typeof json[i]['lastpaid'] != 'undefined')
json[i]['lastpaid'] = new Date((json[i]['lastpaid']) * 1000).toLocaleString();
else
json[i]['lastpaid'] = '<em>N/A</em>';
if (json[i]['activetime'])
json[i]['activetime'] = secondsToHms(json[i]['activetime']);
else
json[i]['activetime'] = '<em>N/A</em>';
json[i]['addr'] = "<a href='/address/" + json[i]['addr'] + "'>" + json[i]['addr'] + (json[i]['claim_name'] != null && json[i]['claim_name'] != '' ? ' <span class="small">(' + json[i]['claim_name'] + ')</span>' : '') + "</a>";
if (labels[addr] != null) {
if (labels[addr].type)
json[i]['addr'] = '<div><label class="badge badge-' + labels[addr].type + '" style="margin-bottom:10px;">' + labels[addr].label + (labels[addr].url ? '<a href="' + labels[addr].url + '" target="_blank", alt="Visit site", title="Visit site" data-toggle="tooltip" data-placement="top"><span class="fa fa-question-circle" style="margin-left:5px"></span></a>' : '') + '</label></div>' + json[i]['addr'];
else
json[i]['addr'] = '<div><label class="badge badge-default" style="margin-bottom:10px;">' + labels[addr].label + (labels[addr].url ? '<a href="' + labels[addr].url + '" target="_blank", alt="Visit site", title="Visit site" data-toggle="tooltip" data-placement="top"><span class="fa fa-question-circle" style="margin-left:5px"></span></a>' : '') + '</label></div>' + json[i]['addr'];
}
}
return json;
}
},
columns: [
{ data: 'rank' },
{ data: 'network' },
{ data: 'addr' },
{ data: 'version' },
{ data: 'lastseen' },
{ data: 'lastpaid' },
{ data: 'activetime' },
{ data: 'status' }
],
rowCallback: function(row, data, index) {
$("td:eq(2)", row).addClass("breakWord");
switch (data['status'].toUpperCase()) {
case 'ENABLED':
$("td:eq(7)", row).addClass('bg-success');
break;
case 'REMOVE':
$("td:eq(7)", row).addClass('bg-danger');
break;
case 'EXPIRED':
$("td:eq(7)", row).addClass('bg-warning');
break;
default:
$("td:eq(7)", row).addClass('bg-info');
break;
}
},
fnDrawCallback: function(settings) {
fixDataTableColumns();
fixFooterHeightAndPosition();
}
});
});
.col-md-12.cardSpacer
.text-center(style='margin-bottom:15px;')
i The current listing of all masternodes known to be active on the network.
div.font-weight-bold(style='margin-bottom:15px;') Last updated:
span.font-weight-normal=(last_updated == null ? ' N/A' : ' ' + format_unixtime(last_updated))
.card.card-default
.card-header
strong Masternodes
table#masternodes-table.table.table-bordered.table-striped.table-hover
- var theadClasses = [];
if settings.display.table_header_bgcolor != null && settings.display.table_header_bgcolor != ''
- theadClasses.push('thead-' + settings.display.table_header_bgcolor);
thead(class=theadClasses)
tr
th.text-center Pay rank
th.text-center Protocol
th.text-center Address
th.text-center Protocol
th.text-center Last seen
th.text-center Last paid
th.text-center Active since
th.text-center Status
tbody.text-center