Files
purple-explorer/lib/database.js
T
joeuhren 2fd39d76ce Add new "last updated date" settings
-Added new configurable settings to allow displaying a "last updated date" timestamp for: index, reward, masternode, movement, network, richlist and market pages
-Affected pages are now configured to be completely hidden and disabled when their display settings are set to false
2020-12-31 15:19:48 -07:00

1305 lines
40 KiB
JavaScript

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')
, Richlist = require('../models/richlist')
, Peers = require('../models/peers')
, Heavy = require('../models/heavy')
, lib = require('./explorer')
, settings = require('./settings')
, fs = require('fs')
, coindesk = require('./apis/coindesk')
, async = require('async');
function find_address(hash, caseSensitive, cb) {
if (caseSensitive) {
// faster search but only matches exact string including case
Address.findOne({a_id: hash}, function(err, address) {
if (address)
return cb(address);
else
return cb();
});
} else {
// slower search but matches exact string ignoring case
Address.findOne({a_id: {$regex: '^' + hash + '$', $options: 'i'}}, function(err, address) {
if (address)
return cb(address);
else
return cb();
});
}
}
function find_address_tx(address, hash, cb) {
AddressTx.findOne({a_id: address, txid: hash}, function(err, address_tx) {
if(address_tx) {
return cb(address_tx);
} else {
return cb();
}
});
}
function find_richlist(coin, cb) {
Richlist.findOne({coin: coin}, function(err, richlist) {
if(richlist) {
return cb(richlist);
} else {
return cb();
}
});
}
function update_address(hash, blockheight, txid, amount, type, cb) {
var to_sent = false;
var to_received = false;
var addr_inc = {}
if ( hash == 'coinbase' ) {
addr_inc.sent = amount;
} else {
if (type == 'vin') {
addr_inc.sent = amount;
addr_inc.balance = -amount;
} else {
addr_inc.received = amount;
addr_inc.balance = amount;
}
}
Address.findOneAndUpdate({a_id: hash}, {
$inc: addr_inc
}, {
new: true,
upsert: true
}, function (err, address) {
if (err) {
return cb(err);
} else {
if ( hash != 'coinbase' ) {
AddressTx.findOneAndUpdate({a_id: hash, txid: txid}, {
$inc: {
amount: addr_inc.balance
},
$set: {
a_id: hash,
blockindex: blockheight,
txid: txid
}
}, {
new: true,
upsert: true
}, function (err,addresstx) {
if (err) {
return cb(err);
} else {
return cb();
}
});
} else {
return cb();
}
}
});
}
function find_tx(txid, cb) {
Tx.findOne({txid: txid}, function(err, tx) {
if(tx) {
return cb(tx);
} else {
return cb(null);
}
});
}
function save_tx(txid, blockheight, cb) {
lib.get_rawtransaction(txid, function(tx){
if (tx && tx != 'There was an error. Check your console.') {
lib.prepare_vin(tx, function(vin) {
lib.prepare_vout(tx.vout, txid, vin, ((typeof tx.vjoinsplit === 'undefined' || tx.vjoinsplit == null) ? [] : tx.vjoinsplit), function(vout, nvin) {
lib.syncLoop(vin.length, function (loop) {
var i = loop.iteration();
update_address(nvin[i].addresses, blockheight, txid, nvin[i].amount, 'vin', function(){
loop.next();
});
}, function(){
lib.syncLoop(vout.length, function (subloop) {
var t = subloop.iteration();
if (vout[t].addresses) {
update_address(vout[t].addresses, blockheight, txid, vout[t].amount, 'vout', function(){
subloop.next();
});
} else {
subloop.next();
}
}, function(){
lib.calculate_total(vout, function(total){
var newTx = new Tx({
txid: tx.txid,
vin: nvin,
vout: vout,
total: total.toFixed(8),
timestamp: tx.time,
blockhash: tx.blockhash,
blockindex: blockheight,
});
newTx.save(function(err) {
if (err) {
return cb(err, false);
} else {
return cb(null, vout.length > 0);
}
});
});
});
});
});
});
} else {
return cb('tx not found: ' + txid, false);
}
});
}
function get_market_data(market, cb) {
if (fs.existsSync('./lib/markets/' + market + '.js')) {
exMarket = require('./markets/' + market);
exMarket.get_data(settings.markets, function(err, obj) {
return cb(err, obj);
});
} else {
return cb(null);
}
}
function create_lock(lockfile, cb) {
if (settings.lock_during_index == true) {
var fname = './tmp/' + lockfile + '.pid';
fs.appendFile(fname, process.pid.toString(), function (err) {
if (err) {
console.log("Error: unable to create %s", fname);
process.exit(1);
} else {
return cb();
}
});
} else {
return cb();
}
}
function remove_lock(lockfile, cb) {
if (settings.lock_during_index == true) {
var fname = './tmp/' + lockfile + '.pid';
fs.unlink(fname, function (err){
if(err) {
console.log("unable to remove lock: %s", fname);
process.exit(1);
} else {
return cb();
}
});
} else {
return cb();
}
}
function is_locked(lockfile, cb) {
if (settings.lock_during_index == true) {
var fname = './tmp/' + lockfile + '.pid';
fs.exists(fname, function (exists){
if(exists) {
return cb(true);
} else {
return cb(false);
}
});
} else {
return cb(false);
}
}
module.exports = {
// initialize DB
connect: function(database, cb) {
mongoose.connect(database, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: false }, function(err) {
if (err) {
console.log('Unable to connect to database: %s', database);
console.log('Aborting');
process.exit(1);
}
//console.log('Successfully connected to MongoDB');
return cb();
});
},
is_locked: function(cb) {
is_locked("db_index", function (exists) {
if (exists) {
return cb(true);
} else {
return cb(false);
}
});
},
check_show_sync_message: function() {
return fs.existsSync('./tmp/show_sync_message.tmp');
},
update_label: function(hash, claim_name, cb) {
find_address(hash, false, function(address) {
if (address) {
Address.updateOne({a_id: hash}, {
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;
// loop through received addresses
for (r = 0; r < richlist.received.length; r++) {
// check if this is the correct address
if (richlist.received[r].a_id == hash) {
// update the claim name
richlist.received[r]['name'] = claim_name;
// mark as updated
updated = true;
}
}
// loop through balance addresses
for (b = 0; b < richlist.balance.length; b++) {
// check if this is the correct address
if (richlist.balance[b].a_id == hash) {
// update the claim name
richlist.balance[b]['name'] = claim_name;
// mark as updated
updated = true;
}
}
// check if the address was updated in the richlist
if (updated) {
// save the richlist back to collection
Richlist.updateOne({coin: settings.coin}, {
received: richlist.received,
balance: richlist.balance
}, function() {
// finished updating the claim label
return cb('');
});
} else {
// finished updating the claim label
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 {
// finished updating the claim label
return cb('');
}
});
} else {
// masternode list is not enabled so nothing to update
return cb('');
}
},
check_stats: function(coin, cb) {
Stats.findOne({coin: coin}, function(err, stats) {
if(stats) {
// collection exists, now check if it is missing the last_usd_price column
Stats.findOne({last_usd_price: {$exists: false}}, function(err, stats) {
if (stats) {
// the last_usd_price needs to be added to the collection
Stats.updateOne({coin: coin}, {
last_usd_price: 0,
}, function() { return cb(null); });
}
});
return cb(true);
} else {
return cb(false);
}
});
},
get_stats: function(coin, cb) {
Stats.findOne({coin: coin}, function(err, stats) {
if(stats) {
return cb(stats);
} else {
return cb(null);
}
});
},
create_stats: function(coin, cb) {
var newStats = new Stats({
coin: coin,
last: 0
});
newStats.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial stats entry created for %s", coin);
//console.log(newStats);
return cb();
}
});
},
get_address: function(hash, caseSensitive, cb) {
find_address(hash, caseSensitive, function(address){
return cb(address);
});
},
get_richlist: function(coin, cb) {
find_richlist(coin, function(richlist){
return cb(richlist);
});
},
// 'list' variable can be either 'received' or 'balance'
update_richlist: function(list, cb){
// Create the burn address array so that we omit burned coins from the rich list
var oBurnAddresses = [];
for (var x = 0; x < settings.burned_coins.length; x++) {
oBurnAddresses.push(settings.burned_coins[x].address);
}
// always omit the private address from the richlist
oBurnAddresses.push("private_tx");
if (list == 'received') {
// Update 'received' richlist data
Address.find({a_id: { $nin: oBurnAddresses }}, 'a_id name balance received').sort({received: 'desc'}).limit(100).exec(function(err, addresses) {
Richlist.updateOne({coin: settings.coin}, {
received: addresses
}, function() {
return cb();
});
});
} else {
// Update 'balance' richlist data
Address.find({a_id: { $nin: oBurnAddresses }}, 'a_id name balance received').sort({balance: 'desc'}).limit(100).exec(function(err, addresses) {
Richlist.updateOne({coin: settings.coin}, {
balance: addresses
}, function() {
return cb();
});
});
}
},
get_tx: function(txid, cb) {
find_tx(txid, function(tx){
return cb(tx);
});
},
get_txs: function(block, cb) {
var txs = [];
lib.syncLoop(block.tx.length, function (loop) {
var i = loop.iteration();
find_tx(block.tx[i], function(tx){
if (tx) {
txs.push(tx);
loop.next();
} else {
loop.next();
}
})
}, function(){
return cb(txs);
});
},
create_txs: function(block, cb) {
is_locked("db_index", function (exists) {
if (exists) {
console.log("db_index lock file exists...");
return cb();
} else {
lib.syncLoop(block.tx.length, function (loop) {
var i = loop.iteration();
save_tx(block.tx[i], block.height, function(err, tx_has_vout) {
if (err) {
loop.next();
} else {
//console.log('tx stored: %s', block.tx[i]);
loop.next();
}
});
}, function(){
return cb();
});
}
});
},
get_last_txs: function(start, length, min, cb) {
this.get_last_txs_ajax(start, length, min, function(txs, count) {
var data = [];
for(i=0; i<txs.length; i++) {
var row = [];
row.push(txs[i].blockindex);
row.push(txs[i].blockhash);
row.push(txs[i].txid);
row.push(txs[i].vout.length);
row.push((txs[i].total));
row.push(new Date((txs[i].timestamp) * 1000).toUTCString());
data.push(row);
}
return cb(data, count);
});
},
get_last_txs_ajax: function(start, length, min, cb) {
// check if min is greater than zero
if (min > 0) {
// min is greater than zero which means we must pull record count from the txes collection
Tx.find({'total': {$gte: min}}).countDocuments(function(err, count) {
// Get last transactions where there is at least 1 vout
Tx.find({'total': {$gte: min}, 'vout': { $gte: { $size: 1 }}}).sort({blockindex: -1}).skip(Number(start)).limit(Number(length)).exec(function(err, txs) {
if (err) {
return cb(err);
} else {
return cb(txs, count);
}
});
});
} else {
// min is zero (shouldn't ever be negative) which means we must pull record count from the coinstats collection (pulling from txes could potentially take a long time because it would include coinbase txes)
Stats.findOne({coin:settings.coin}, function(err, stats) {
// Get last transactions where there is at least 1 vout
Tx.find({'total': {$gte: min}, 'vout': { $gte: { $size: 1 }}}).sort({blockindex: -1}).skip(Number(start)).limit(Number(length)).exec(function(err, txs) {
if (err) {
return cb(err);
} else {
return cb(txs, stats.txes);
}
});
});
}
},
get_address_txs_ajax: function(hash, start, length, cb) {
var totalCount = 0;
AddressTx.find({a_id: hash}).countDocuments(function(err, count){
if(err) {
return cb(err);
} else {
totalCount = count;
AddressTx.aggregate([
{ $match: { a_id: hash } },
{ $sort: {blockindex: -1} },
{ $skip: Number(start) },
{
$group: {
_id: '',
balance: { $sum: '$amount' }
}
},
{
$project: {
_id: 0,
balance: '$balance'
}
},
{ $sort: {blockindex: -1} }
], function (err,balance_sum) {
if (err) {
return cb(err);
} else {
AddressTx.find({a_id: hash}).sort({blockindex: -1}).skip(Number(start)).limit(Number(length)).exec(function (err, address_tx) {
if (err) {
return cb(err);
} else {
var txs = [];
var count = address_tx.length;
var running_balance = balance_sum.length > 0 ? balance_sum[0].balance : 0;
var txs = [];
lib.syncLoop(count, function (loop) {
var i = loop.iteration();
find_tx(address_tx[i].txid, function (tx) {
if (tx && !txs.includes(tx)) {
tx.balance = running_balance;
txs.push(tx);
loop.next();
} else if (!txs.includes(tx)) {
txs.push("1. Not found");
loop.next();
} else {
loop.next();
}
running_balance = running_balance - address_tx[i].amount;
})
}, function () {
return cb(txs, totalCount);
});
}
});
}
});
}
});
},
create_market: function(coin, exchange, market, cb) {
var newMarkets = new Markets({
market: market,
coin: coin,
exchange: exchange,
});
newMarkets.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial markets entry created for %s", market);
//console.log(newMarkets);
return cb();
}
});
},
// checks market data exists for given market
check_market: function(market, cb) {
Markets.findOne({market: market}, function(err, exists) {
if(exists) {
return cb(market, true);
} else {
return cb(market, false);
}
});
},
// gets market data for given market
get_market: function(market, cb) {
Markets.findOne({market: market}, function(err, data) {
if(data) {
return cb(data);
} else {
return cb(null);
}
});
},
// creates initial richlist entry in database; called on first launch of explorer
create_richlist: function(coin, cb) {
var newRichlist = new Richlist({
coin: coin,
});
newRichlist.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial richlist entry created for %s", coin);
//console.log(newRichlist);
return cb();
}
});
},
// drops richlist data for given coin
delete_richlist: function(coin, cb) {
Richlist.findOneAndRemove({coin: coin}, function(err, exists) {
if(exists) {
return cb(true);
} else {
return cb(false);
}
});
},
// checks richlist data exists for given coin
check_richlist: function(coin, cb) {
Richlist.findOne({coin: coin}, function(err, exists) {
if(exists) {
return cb(true);
} else {
return cb(false);
}
});
},
create_heavy: function(coin, cb) {
var newHeavy = new Heavy({
coin: coin,
});
newHeavy.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial heavy entry created for %s", coin);
console.log(newHeavy);
return cb();
}
});
},
check_heavy: function(coin, cb) {
Heavy.findOne({coin: coin}, function(err, exists) {
if(exists) {
return cb(true);
} else {
return cb(false);
}
});
},
get_heavy: function(coin, cb) {
Heavy.findOne({coin: coin}, function(err, heavy) {
if(heavy) {
return cb(heavy);
} else {
return cb(null);
}
});
},
get_distribution: function(richlist, stats, cb){
var distribution = {
supply: stats.supply,
t_1_25: {percent: 0, total: 0 },
t_26_50: {percent: 0, total: 0 },
t_51_75: {percent: 0, total: 0 },
t_76_100: {percent: 0, total: 0 },
t_101plus: {percent: 0, total: 0 }
};
lib.syncLoop(richlist.balance.length, function (loop) {
var i = loop.iteration();
var count = i + 1;
var percentage = ((richlist.balance[i].balance / 100000000) / stats.supply) * 100;
if (count <= 25 ) {
distribution.t_1_25.percent = distribution.t_1_25.percent + percentage;
distribution.t_1_25.total = distribution.t_1_25.total + (richlist.balance[i].balance / 100000000);
}
if (count <= 50 && count > 25) {
distribution.t_26_50.percent = distribution.t_26_50.percent + percentage;
distribution.t_26_50.total = distribution.t_26_50.total + (richlist.balance[i].balance / 100000000);
}
if (count <= 75 && count > 50) {
distribution.t_51_75.percent = distribution.t_51_75.percent + percentage;
distribution.t_51_75.total = distribution.t_51_75.total + (richlist.balance[i].balance / 100000000);
}
if (count <= 100 && count > 75) {
distribution.t_76_100.percent = distribution.t_76_100.percent + percentage;
distribution.t_76_100.total = distribution.t_76_100.total + (richlist.balance[i].balance / 100000000);
}
loop.next();
}, function(){
distribution.t_101plus.percent = parseFloat(100 - distribution.t_76_100.percent - distribution.t_51_75.percent - distribution.t_26_50.percent - distribution.t_1_25.percent).toFixed(2);
distribution.t_101plus.total = parseFloat(distribution.supply - distribution.t_76_100.total - distribution.t_51_75.total - distribution.t_26_50.total - distribution.t_1_25.total).toFixed(8);
distribution.t_1_25.percent = parseFloat(distribution.t_1_25.percent).toFixed(2);
distribution.t_1_25.total = parseFloat(distribution.t_1_25.total).toFixed(8);
distribution.t_26_50.percent = parseFloat(distribution.t_26_50.percent).toFixed(2);
distribution.t_26_50.total = parseFloat(distribution.t_26_50.total).toFixed(8);
distribution.t_51_75.percent = parseFloat(distribution.t_51_75.percent).toFixed(2);
distribution.t_51_75.total = parseFloat(distribution.t_51_75.total).toFixed(8);
distribution.t_76_100.percent = parseFloat(distribution.t_76_100.percent).toFixed(2);
distribution.t_76_100.total = parseFloat(distribution.t_76_100.total).toFixed(8);
return cb(distribution);
});
},
// updates heavy stats for coin
// height: current block height, count: amount of votes to store
update_heavy: function(coin, height, count, cb) {
var newVotes = [];
lib.get_maxmoney( function (maxmoney) {
lib.get_maxvote( function (maxvote) {
lib.get_vote( function (vote) {
lib.get_phase( function (phase) {
lib.get_reward( function (reward) {
lib.get_supply( function (supply) {
lib.get_estnext( function (estnext) {
lib.get_nextin( function (nextin) {
lib.syncLoop(count, function (loop) {
var i = loop.iteration();
lib.get_blockhash(height - i, function (hash) {
lib.get_block(hash, function (block) {
newVotes.push({ count: height - i, reward: block.reward, vote: (block && block.vote ? block.vote : 0) });
loop.next();
});
});
}, function() {
Heavy.updateOne({coin: coin}, {
lvote: (vote ? vote : 0),
reward: (reward ? reward : 0),
supply: (supply ? supply : 0),
cap: (maxmoney ? maxmoney : 0),
estnext: (estnext ? estnext : 0),
phase: (phase ? phase : 'N/A'),
maxvote: (maxvote ? maxvote : 0),
nextin: (nextin ? nextin : 'N/A'),
votes: newVotes
}, function() {
// update reward_last_updated value
module.exports.update_last_updated_stats(settings.coin, { reward_last_updated: Math.floor(new Date() / 1000) }, function (new_cb) {
console.log('heavy update complete');
return cb();
});
});
});
});
});
});
});
});
});
});
});
},
// updates market data for given market; called by sync.js
update_markets_db: function(market, cb) {
// check if market exists
if (fs.existsSync('./lib/markets/' + market + '.js')) {
get_market_data(market, function (err, obj) {
if (err == null) {
Markets.updateOne({market:market}, {
chartdata: JSON.stringify(obj.chartdata),
buys: obj.buys,
sells: obj.sells,
history: obj.trades,
summary: obj.stats
}, function() {
if ( market == settings.markets.default ) {
Stats.updateOne({coin:settings.coin}, {
last_price: obj.stats.last,
}, function(){
return cb(null);
});
} else {
return cb(null);
}
});
} else {
return cb(err);
}
});
} else {
// market does not exist
return cb('market is not installed');
}
},
get_last_usd_price: function(cb) {
// Check if the market price is being recorded in BTC
if (settings.markets.enabled.length > 0 && settings.markets.exchange.toLowerCase() == "btc") {
// Convert btc to usd via coindesk api
coindesk.get_data(function (err, last_usd) {
// Get current stats
Stats.findOne({coin:settings.coin}, function(err, stats) {
// Update the last usd price
Stats.updateOne({coin:settings.coin}, {
last_usd_price: (last_usd * stats.last_price),
}, function(){
return cb(null);
});
});
});
} else {
return cb(null);
}
},
// updates stats data for given coin; called by sync.js
update_db: function(coin, cb) {
lib.get_blockcount( function (count) {
if (!count) {
console.log('Unable to connect to explorer API');
return cb(false);
}
lib.get_supply( function (supply) {
lib.get_connectioncount(function (connections) {
Stats.findOne({coin: coin}, function(err, stats) {
if (stats) {
Stats.updateOne({coin: coin}, {
coin: coin,
count : count,
supply: (supply ? supply : 0),
connections: (connections ? connections : 0)
}, function(err) {
if (err) {
console.log("Error during Stats Update: ", err);
}
return cb({
coin: coin,
count : count,
supply: (supply ? supply : 0),
connections: (connections ? connections : 0),
last: (stats.last ? stats.last : 0),
txes: (stats.txes ? stats.txes : 0)
});
});
} else {
console.log("Error during Stats Update: ", (err ? err : 'cannot find stats collection'));
return cb(false);
}
});
});
});
});
},
// updates tx, address & richlist db's; called by sync.js
update_tx_db: function(coin, start, end, txes, timeout, cb) {
is_locked("db_index", function (exists) {
if (exists) {
console.log("db_index lock file exists...");
return cb();
} else {
create_lock("db_index", function (){
var complete = false;
var blocks_to_scan = [];
var task_limit_blocks = settings.block_parallel_tasks;
if (typeof start === 'undefined' || start < 1) start = 1; // fix for invalid block height (skip genesis block as it should not have valid txs)
if (task_limit_blocks < 1) { task_limit_blocks = 1; }
var task_limit_txs = 1;
for (i=start; i<(end+1); i++) {
blocks_to_scan.push(i);
}
async.eachLimit(blocks_to_scan, task_limit_blocks, function(block_height, next_block) {
if (block_height % settings.save_stats_after_sync_blocks === 0) {
Stats.updateOne({coin: coin}, {
last: block_height - 1,
txes: txes,
last_txs: '' //not used anymore left to clear out existing objects
}, function() {});
}
lib.get_blockhash(block_height, function(blockhash){
if (blockhash) {
lib.get_block(blockhash, function(block) {
if (block) {
async.eachLimit(block.tx, task_limit_txs, function(txid, next_tx) {
Tx.findOne({txid: txid}, function(err, tx) {
if(tx) {
setTimeout( function(){
tx = null;
next_tx();
}, timeout);
} else {
save_tx(txid, block_height, function(err, tx_has_vout) {
if (err) {
console.log(err);
} else {
console.log('%s: %s', block_height, txid);
}
if (tx_has_vout)
txes++;
setTimeout( function(){
tx = null;
next_tx();
}, timeout);
});
}
});
}, function(){
setTimeout( function(){
blockhash = null;
block = null;
next_block();
}, timeout);
});
} else {
console.log('block not found: %s', blockhash);
setTimeout( function(){
next_block();
}, timeout);
}
});
} else {
setTimeout( function(){
next_block();
}, timeout);
}
});
}, function(){
Tx.find({}).sort({timestamp: 'desc'}).limit(settings.index.last_txs).exec(function(err, txs){
Stats.updateOne({coin: coin}, {
last: end,
txes: txes,
last_txs: '' //not used anymore left to clear out existing objects
}, function() {
remove_lock("db_index", function(){
return cb();
});
});
});
});
});
}
});
},
create_peer: function(params, cb) {
var newPeer = new Peers(params);
newPeer.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
return cb();
}
});
},
find_peer: function(address, cb) {
Peers.findOne({address: address}, function(err, peer) {
if (err) {
return cb(null);
} else {
if (peer) {
return cb(peer);
} else {
return cb (null)
}
}
})
},
drop_peer: function(address, cb) {
Peers.deleteOne({address: address}, function(err) {
if (err) {
console.log(err);
return cb();
} else {
return cb ()
}
})
},
drop_peers: function(cb) {
Peers.deleteMany({}, function(err) {
if (err) {
console.log(err);
return cb();
} else {
return cb ()
}
})
},
get_peers: function(cb) {
Peers.find({}, function(err, peers) {
if (err) {
return cb([]);
} else {
return cb(peers);
}
});
},
// 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);
});
},
get_masternode_rewards: function(mnPayees, since, cb) {
Tx.aggregate([
{ $match: {
"blockindex": { $gt: Number(since) },
"vin": []
}},
{ "$unwind": "$vout" },
{ $match: {
"vout.addresses": { $in: [mnPayees] }
}}
], function(err, data) {
if (err) {
console.log(err);
return cb(null);
} else
return cb(data);
});
},
get_masternode_rewards_totals: function(mnPayees, since, cb) {
Tx.aggregate([
{ $match: {
"blockindex": { $gt: Number(since) },
"vin": []
}},
{ "$unwind": "$vout" },
{ $match: {
"vout.addresses": { $in: [mnPayees] }
}},
{ $group: { _id: null, total: { $sum: "$vout.amount" } } }
], function(err, data) {
if (err) {
console.log(err);
return cb(null);
} else
return cb((data.length > 0 ? data[0].total / 100000000 : 0));
});
},
// updates last_updated stats; called by sync.js
update_last_updated_stats: function (coin, param, cb) {
if (param.blockchain_last_updated) {
// update blockchain last updated date
Stats.updateOne({ coin: coin }, {
blockchain_last_updated: param.blockchain_last_updated
}, function () {
return cb(true);
});
} else if (param.reward_last_updated) {
// update reward last updated date
Stats.updateOne({ coin: coin }, {
reward_last_updated: param.reward_last_updated
}, function () {
return cb(true);
});
} else 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 if (param.network_last_updated) {
// update network last updated date
Stats.updateOne({ coin: coin }, {
network_last_updated: param.network_last_updated
}, function () {
return cb(true);
});
} else if (param.richlist_last_updated) {
// update richlist last updated date
Stats.updateOne({ coin: coin }, {
richlist_last_updated: param.richlist_last_updated
}, function () {
return cb(true);
});
} else if (param.markets_last_updated) {
// update markets last updated date
Stats.updateOne({ coin: coin }, {
markets_last_updated: param.markets_last_updated
}, function () {
return cb(true);
});
} else {
// invalid option
return cb(false);
}
},
populate_claim_address_names: function(tx, cb) {
var addresses = [];
// loop through vin addresses
tx.vin.forEach(function (vin) {
// check if this address already exists
if (addresses.indexOf(vin.addresses) == -1) {
// add address to array
addresses.push(vin.addresses);
}
});
// loop through vout addresses
tx.vout.forEach(function (vout) {
// check if this address already exists
if (addresses.indexOf(vout.addresses) == -1) {
// add address to array
addresses.push(vout.addresses);
}
});
// loop through address array
lib.syncLoop(addresses.length, function (loop) {
var a = loop.iteration();
module.exports.get_address(addresses[a], false, function(address) {
if (address && address.name != null && address.name != '') {
// look for address in vin
for (v = 0; v < tx.vin.length; v++) {
// check if this is the correct address
if (tx.vin[v].addresses == address.a_id) {
// add claim name to array
tx.vin[v]['claim_name'] = address.name;
}
}
// look for address in vout
for (v = 0; v < tx.vout.length; v++) {
// check if this is the correct address
if (tx.vout[v].addresses == address.a_id) {
// add claim name to array
tx.vout[v]['claim_name'] = address.name;
}
}
}
loop.next();
});
}, function() {
// return modified tx object
return cb(tx);
});
},
fs: fs
};