Files
purple-explorer/lib/database.js
T

1298 lines
41 KiB
JavaScript
Raw Normal View History

2021-03-17 17:54:09 -06:00
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'),
2021-03-20 01:34:13 -06:00
locale = require('./locale'),
2021-03-17 17:54:09 -06:00
fs = require('fs'),
coindesk = require('./apis/coindesk'),
async = require('async');
2019-05-27 10:33:22 -07:00
2019-10-17 21:51:13 -06:00
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();
});
}
2019-05-27 10:33:22 -07:00
}
function find_address_tx(address, hash, cb) {
AddressTx.findOne({a_id: address, txid: hash}, function(err, address_tx) {
2021-03-17 17:54:09 -06:00
if (address_tx)
return cb(address_tx);
2021-03-17 17:54:09 -06:00
else
return cb();
});
}
2019-05-27 10:33:22 -07:00
function find_richlist(coin, cb) {
Richlist.findOne({coin: coin}, function(err, richlist) {
2021-03-17 17:54:09 -06:00
if (richlist)
2019-05-27 10:33:22 -07:00
return cb(richlist);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb();
});
}
function update_address(hash, blockheight, txid, amount, type, cb) {
var to_sent = false;
var to_received = false;
var addr_inc = {}
2021-03-17 17:54:09 -06:00
if (hash == 'coinbase')
addr_inc.sent = amount;
2021-03-17 17:54:09 -06:00
else {
if (type == 'vin') {
addr_inc.sent = amount;
addr_inc.balance = -amount;
2019-05-27 10:33:22 -07:00
} else {
addr_inc.received = amount;
addr_inc.balance = amount;
}
}
2021-03-17 17:54:09 -06:00
Address.findOneAndUpdate({a_id: hash}, {
$inc: addr_inc
}, {
new: true,
upsert: true
}, function (err, address) {
2021-03-17 17:54:09 -06:00
if (err)
return cb(err);
2021-03-17 17:54:09 -06:00
else {
if (hash != 'coinbase') {
2020-11-22 17:35:57 -07:00
AddressTx.findOneAndUpdate({a_id: hash, txid: txid}, {
2020-11-22 18:27:04 -07:00
$inc: {
amount: addr_inc.balance
},
2020-11-22 17:35:57 -07:00
$set: {
a_id: hash,
blockindex: blockheight,
2020-11-22 18:27:04 -07:00
txid: txid
2020-11-22 17:35:57 -07:00
}
}, {
new: true,
upsert: true
}, function (err,addresstx) {
2021-03-17 17:54:09 -06:00
if (err)
2020-11-22 17:35:57 -07:00
return cb(err);
2021-03-17 17:54:09 -06:00
else
2020-11-22 17:35:57 -07:00
return cb();
});
2021-03-17 17:54:09 -06:00
} else
2020-11-22 17:35:57 -07:00
return cb();
2019-05-27 10:33:22 -07:00
}
});
}
function find_tx(txid, cb) {
Tx.findOne({txid: txid}, function(err, tx) {
2021-03-17 17:54:09 -06:00
if (tx)
2019-05-27 10:33:22 -07:00
return cb(tx);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(null);
});
}
function save_tx(txid, blockheight, cb) {
2021-01-22 15:04:32 -07:00
lib.get_rawtransaction(txid, function(tx) {
if (tx && tx != 'There was an error. Check your console.') {
2021-03-20 01:34:13 -06:00
lib.prepare_vin(tx, function(vin, tx_type_vin) {
lib.prepare_vout(tx.vout, txid, vin, ((!settings.blockchain_specific.zksnarks.enabled || typeof tx.vjoinsplit === 'undefined' || tx.vjoinsplit == null) ? [] : tx.vjoinsplit), function(vout, nvin, tx_type_vout) {
lib.syncLoop(vin.length, function (loop) {
var i = loop.iteration();
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
update_address(nvin[i].addresses, blockheight, txid, nvin[i].amount, 'vin', function() {
loop.next();
});
2021-01-22 15:04:32 -07:00
}, function() {
lib.syncLoop(vout.length, function (subloop) {
var t = subloop.iteration();
2021-03-17 17:54:09 -06:00
if (vout[t].addresses) {
2021-01-22 15:04:32 -07:00
update_address(vout[t].addresses, blockheight, txid, vout[t].amount, 'vout', function() {
subloop.next();
2019-05-27 10:33:22 -07:00
});
2021-01-22 15:04:32 -07:00
} else
subloop.next();
2021-01-22 15:04:32 -07:00
}, 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,
2021-03-20 01:34:13 -06:00
blockindex: blockheight,
tx_type: (tx_type_vout == null ? tx_type_vin : tx_type_vout)
});
2021-03-17 17:54:09 -06:00
newTx.save(function(err) {
2021-01-22 15:04:32 -07:00
if (err)
return cb(err, false);
2021-01-22 15:04:32 -07:00
else
return cb(null, vout.length > 0);
2019-05-27 10:33:22 -07:00
});
});
});
});
});
2019-05-27 10:33:22 -07:00
});
2021-01-22 15:04:32 -07:00
} else
return cb('tx not found: ' + txid, false);
2019-05-27 10:33:22 -07:00
});
}
2021-01-22 15:04:32 -07:00
function get_market_data(market, coin_symbol, pair_symbol, cb) {
2020-11-27 20:34:15 -07:00
if (fs.existsSync('./lib/markets/' + market + '.js')) {
exMarket = require('./markets/' + market);
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
exMarket.get_data({coin: coin_symbol, exchange: pair_symbol}, function(err, obj) {
return cb(err, obj);
});
2021-01-22 15:04:32 -07:00
} else
return cb(null);
}
2021-03-20 01:34:13 -06:00
function check_add_db_field(model_obj, field_name, default_value, cb) {
// determine if a particular field exists in a db collection
model_obj.findOne({[field_name]: {$exists: false}}, function(err, model_data) {
// check if field exists
if (model_data) {
// add field to all documents in the collection
model_obj.updateMany({}, {
$set: { [field_name]: default_value }
}, function() {
return cb(true);
});
} else
return cb(false);
});
}
2019-05-27 10:33:22 -07:00
module.exports = {
// initialize DB
connect: function(database, cb) {
mongoose.connect(database, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true, useFindAndModify: false }, function(err) {
2019-05-27 10:33:22 -07:00
if (err) {
console.log('Unable to connect to database: %s', database);
console.log('Aborting');
process.exit(1);
}
2021-01-22 15:04:32 -07:00
return cb();
});
},
check_show_sync_message: function() {
2020-12-05 18:21:21 -07:00
return fs.existsSync('./tmp/show_sync_message.tmp');
},
2020-12-30 18:22:02 -07:00
update_label: function(hash, claim_name, cb) {
2020-12-23 18:40:10 -07:00
find_address(hash, false, function(address) {
if (address) {
Address.updateOne({a_id: hash}, {
2020-12-30 18:22:02 -07:00
name: claim_name
2020-12-23 18:40:10 -07:00
}, function() {
2020-12-30 18:22:02 -07:00
// 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() {
2020-12-26 22:01:36 -07:00
return cb('');
2020-12-30 18:22:02 -07:00
});
2020-12-23 18:40:10 -07:00
});
2020-11-22 14:39:10 -07:00
});
2020-12-26 22:01:36 -07:00
} else {
// address is not valid or does not have any transactions
return cb('no_address');
2020-11-22 14:39:10 -07:00
}
});
},
2020-12-30 18:22:02 -07:00
update_richlist_claim_name: function(hash, claim_name, cb) {
// check if the richlist is enabled
2021-01-22 15:04:32 -07:00
if (settings.richlist_page.enabled == true) {
2020-12-30 18:22:02 -07:00
// ensure that if this address exists in the richlist that it displays the new alias
2021-01-22 15:04:32 -07:00
module.exports.get_richlist(settings.coin.name, function(richlist) {
2020-12-30 18:22:02 -07:00
var updated = false;
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// 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;
}
}
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// 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
2021-03-17 17:54:09 -06:00
updated = true;
2020-12-30 18:22:02 -07:00
}
}
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// check if the address was updated in the richlist
if (updated) {
// save the richlist back to collection
2021-01-22 15:04:32 -07:00
Richlist.updateOne({coin: settings.coin.name}, {
2020-12-30 18:22:02 -07:00
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
2021-01-22 15:04:32 -07:00
if (settings.masternodes_page.enabled == true) {
2020-12-30 18:22:02 -07:00
// ensure that if this address exists in the masternode that it displays the new alias
module.exports.get_masternodes(function(masternodes) {
var updated = false;
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// 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;
}
}
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// 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('');
}
},
2021-03-20 01:34:13 -06:00
check_txes: function(cb) {
Tx.findOne({}, function(err, tx) {
if (tx) {
// collection has data
// determine if tx_type field exists
check_add_db_field(Tx, 'tx_type', null, function(exists) {
return cb(true);
});
} else
return cb(false);
});
},
2019-05-27 10:33:22 -07:00
check_stats: function(coin, cb) {
Stats.findOne({coin: coin}, function(err, stats) {
2021-03-17 17:54:09 -06:00
if (stats) {
2021-03-20 01:34:13 -06:00
// collection has data
// determine if last_usd_price field exists
check_add_db_field(Stats, 'last_usd_price', 0, function(exists) {
return cb(true);
2021-03-17 17:54:09 -06:00
});
} else
2019-05-27 10:33:22 -07:00
return cb(false);
});
},
get_stats: function(coin, cb) {
Stats.findOne({coin: coin}, function(err, stats) {
2021-03-17 17:54:09 -06:00
if (stats)
2019-05-27 10:33:22 -07:00
return cb(stats);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(null);
});
},
create_stats: function(coin, cb) {
var newStats = new Stats({
coin: coin,
last: 0
2019-05-27 10:33:22 -07:00
});
newStats.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial stats entry created for %s", coin);
return cb();
}
});
},
2019-10-17 21:51:13 -06:00
get_address: function(hash, caseSensitive, cb) {
2021-03-17 17:54:09 -06:00
find_address(hash, caseSensitive, function(address) {
2019-05-27 10:33:22 -07:00
return cb(address);
});
},
get_richlist: function(coin, cb) {
2021-03-17 17:54:09 -06:00
find_richlist(coin, function(richlist) {
2019-05-27 10:33:22 -07:00
return cb(richlist);
});
},
2020-12-23 18:40:10 -07:00
// 'list' variable can be either 'received' or 'balance'
2021-01-22 15:04:32 -07:00
update_richlist: function(list, cb) {
// number of addresses to lookup
var total_addresses = 100;
// create the burn address array so that we omit burned coins from the rich list
var burn_addresses = settings.richlist_page.burned_coins.addresses;
2021-03-17 17:54:09 -06:00
2019-10-18 22:31:07 -06:00
// always omit the private address from the richlist
2021-03-20 01:34:13 -06:00
burn_addresses.push('hidden_address');
2019-10-18 22:31:07 -06:00
2020-12-23 18:40:10 -07:00
if (list == 'received') {
2021-01-22 15:04:32 -07:00
// update 'received' richlist data
Address.find({a_id: { $nin: burn_addresses }}, 'a_id name balance received').sort({received: 'desc'}).limit(total_addresses).exec(function(err, addresses) {
Richlist.updateOne({coin: settings.coin.name}, {
2020-12-23 18:40:10 -07:00
received: addresses
2019-05-27 10:33:22 -07:00
}, function() {
return cb();
});
});
2020-12-23 18:40:10 -07:00
} else {
2021-01-22 15:04:32 -07:00
// update 'balance' richlist data
// check if burned addresses are in use and if it is necessary to track burned balances
if (settings.richlist_page.burned_coins.addresses == null || settings.richlist_page.burned_coins.addresses.length == 0 || !settings.richlist_page.burned_coins.include_burned_coins_in_distribution) {
// update 'balance' richlist data by filtering burned coin addresses immidiately
Address.find({a_id: { $nin: burn_addresses }}, 'a_id name balance received').sort({balance: 'desc'}).limit(total_addresses).exec(function(err, addresses) {
Richlist.updateOne({coin: settings.coin.name}, {
balance: addresses
}, function() {
return cb();
});
2019-05-27 10:33:22 -07:00
});
2021-01-22 15:04:32 -07:00
} else {
// do not omit burned addresses from database query. instead, increase the limit of returned addresses and manually remove each burned address that made it into the rich list after recording the burned balance
Address.find({}, 'a_id name balance received').sort({balance: 'desc'}).limit(total_addresses + burn_addresses.length).exec(function(err, addresses) {
var return_addresses = [];
var burned_balance = 0.0;
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
// loop through all richlist addresses
addresses.forEach(function (address) {
// check if this is a burned coin address
if (burn_addresses.findIndex(p => p.toLowerCase() == address.a_id.toLowerCase()) > -1) {
// this is a burned coin address so save the balance, not the address
burned_balance += address.balance;
} else if (return_addresses.length < total_addresses) {
// this is not a burned address so add it to the return list
return_addresses.push(address);
}
});
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
// update the rich list collection
Richlist.updateOne({coin: settings.coin.name}, {
balance: return_addresses,
burned: burned_balance
}, function() {
return cb();
});
});
}
2019-05-27 10:33:22 -07:00
}
},
get_tx: function(txid, cb) {
2021-03-17 17:54:09 -06:00
find_tx(txid, function(tx) {
2019-05-27 10:33:22 -07:00
return cb(tx);
});
},
get_txs: function(block, cb) {
var txs = [];
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
lib.syncLoop(block.tx.length, function (loop) {
var i = loop.iteration();
2021-03-17 17:54:09 -06:00
find_tx(block.tx[i], function(tx) {
2019-05-27 10:33:22 -07:00
if (tx) {
txs.push(tx);
loop.next();
2021-03-17 17:54:09 -06:00
} else
2019-05-27 10:33:22 -07:00
loop.next();
2021-03-17 17:54:09 -06:00
});
}, function() {
2019-05-27 10:33:22 -07:00
return cb(txs);
});
},
2021-01-01 18:24:39 -07:00
get_last_txs: function(start, length, min, internal, cb) {
this.get_last_txs_ajax(start, length, min, function(txs, count) {
2020-11-28 20:40:37 -07:00
var data = [];
2021-01-01 18:24:39 -07:00
for (i = 0; i < txs.length; i++) {
if (internal) {
var row = [];
2021-03-17 17:54:09 -06:00
2021-01-01 18:24:39 -07:00
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 / 100000000));
row.push(txs[i].timestamp);
2021-03-17 17:54:09 -06:00
2021-01-01 18:24:39 -07:00
data.push(row);
} else {
data.push({
blockindex: txs[i].blockindex,
blockhash: txs[i].blockhash,
txid: txs[i].txid,
recipients: txs[i].vout.length,
amount: (txs[i].total / 100000000),
timestamp: txs[i].timestamp
});
}
2020-11-28 20:40:37 -07:00
}
2021-03-17 17:54:09 -06:00
return cb(data, count);
2020-11-28 20:40:37 -07:00
});
},
get_last_txs_ajax: function(start, length, min, cb) {
2020-12-10 17:51:46 -07:00
// 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) {
2021-03-17 17:54:09 -06:00
// get last transactions where there is at least 1 vout
2020-12-10 17:51:46 -07:00
Tx.find({'total': {$gte: min}, 'vout': { $gte: { $size: 1 }}}).sort({blockindex: -1}).skip(Number(start)).limit(Number(length)).exec(function(err, txs) {
2021-03-17 17:54:09 -06:00
if (err)
2020-12-10 17:51:46 -07:00
return cb(err);
2021-03-17 17:54:09 -06:00
else
2020-12-10 17:51:46 -07:00
return cb(txs, count);
});
2020-11-23 15:35:14 -07:00
});
2020-12-10 17:51:46 -07:00
} 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)
2021-01-22 15:04:32 -07:00
Stats.findOne({coin: settings.coin.name}, function(err, stats) {
2020-12-10 17:51:46 -07:00
// 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) {
2021-03-17 17:54:09 -06:00
if (err)
2020-12-10 17:51:46 -07:00
return cb(err);
2021-03-17 17:54:09 -06:00
else
2020-12-10 17:51:46 -07:00
return cb(txs, stats.txes);
});
});
}
},
get_address_txs_ajax: function(hash, start, length, cb) {
var totalCount = 0;
2021-03-17 17:54:09 -06:00
AddressTx.find({a_id: hash}).countDocuments(function(err, count) {
if (err)
return cb(err);
2021-03-17 17:54:09 -06:00
else {
2020-11-22 18:27:04 -07:00
totalCount = count;
2021-03-17 17:54:09 -06:00
2020-11-22 18:27:04 -07:00
AddressTx.aggregate([
2020-11-23 20:58:34 -07:00
{ $match: { a_id: hash } },
2020-11-22 18:27:04 -07:00
{ $sort: {blockindex: -1} },
2020-11-23 20:58:34 -07:00
{ $skip: Number(start) },
2020-11-22 18:27:04 -07:00
{
$group: {
_id: '',
balance: { $sum: '$amount' }
}
},
{
$project: {
_id: 0,
balance: '$balance'
}
},
{ $sort: {blockindex: -1} }
], function (err,balance_sum) {
2021-03-17 17:54:09 -06:00
if (err)
return cb(err);
2021-03-17 17:54:09 -06:00
else {
2020-11-23 15:35:14 -07:00
AddressTx.find({a_id: hash}).sort({blockindex: -1}).skip(Number(start)).limit(Number(length)).exec(function (err, address_tx) {
2021-03-17 17:54:09 -06:00
if (err)
return cb(err);
2021-03-17 17:54:09 -06:00
else {
var txs = [];
2020-11-22 18:27:04 -07:00
var count = address_tx.length;
2020-11-23 20:58:34 -07:00
var running_balance = balance_sum.length > 0 ? balance_sum[0].balance : 0;
var txs = [];
lib.syncLoop(count, function (loop) {
var i = loop.iteration();
2021-03-17 17:54:09 -06:00
2020-11-23 20:58:34 -07:00
find_tx(address_tx[i].txid, function (tx) {
if (tx && !txs.includes(tx)) {
2020-11-22 18:27:04 -07:00
tx.balance = running_balance;
txs.push(tx);
loop.next();
} else if (!txs.includes(tx)) {
txs.push("1. Not found");
loop.next();
2021-03-17 17:54:09 -06:00
} else
loop.next();
2021-03-17 17:54:09 -06:00
2020-11-22 18:27:04 -07:00
running_balance = running_balance - address_tx[i].amount;
2021-03-17 17:54:09 -06:00
});
}, function () {
return cb(txs, totalCount);
});
}
});
}
});
}
});
},
2021-01-22 15:04:32 -07:00
create_market: function(coin_symbol, pair_symbol, market, cb) {
2019-05-27 10:33:22 -07:00
var newMarkets = new Markets({
market: market,
2021-01-22 15:04:32 -07:00
coin_symbol: coin_symbol,
2021-03-17 17:54:09 -06:00
pair_symbol: pair_symbol
2019-05-27 10:33:22 -07:00
});
newMarkets.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
2021-01-22 15:04:32 -07:00
console.log("initial market entry created for %s: %s", market, coin_symbol +'/' + pair_symbol);
2019-05-27 10:33:22 -07:00
return cb();
}
});
},
2021-01-22 15:04:32 -07:00
// check if market data exists for a given market and trading pair
check_market: function(market, coin_symbol, pair_symbol, cb) {
Markets.findOne({market: market, coin_symbol: coin_symbol, pair_symbol: pair_symbol}, function(err, exists) {
return cb(market, exists);
2019-05-27 10:33:22 -07:00
});
},
2021-01-22 15:04:32 -07:00
// gets market data for given market and trading pair
get_market: function(market, coin_symbol, pair_symbol, cb) {
Markets.findOne({market: market, coin_symbol: coin_symbol, pair_symbol: pair_symbol}, function(err, data) {
if (data)
2019-05-27 10:33:22 -07:00
return cb(data);
2021-01-22 15:04:32 -07:00
else
2019-05-27 10:33:22 -07:00
return cb(null);
});
},
// creates initial richlist entry in database; called on first launch of explorer
create_richlist: function(coin, cb) {
var newRichlist = new Richlist({
2021-03-17 17:54:09 -06:00
coin: coin
2019-05-27 10:33:22 -07:00
});
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
newRichlist.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
console.log("initial richlist entry created for %s", coin);
return cb();
}
});
},
// drops richlist data for given coin
delete_richlist: function(coin, cb) {
Richlist.findOneAndRemove({coin: coin}, function(err, exists) {
2021-03-17 17:54:09 -06:00
if (exists)
return cb(true);
2021-03-17 17:54:09 -06:00
else
return cb(false);
});
},
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
// checks richlist data exists for given coin
check_richlist: function(coin, cb) {
Richlist.findOne({coin: coin}, function(err, exists) {
2021-03-17 17:54:09 -06:00
if (exists)
2019-05-27 10:33:22 -07:00
return cb(true);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(false);
});
},
create_heavy: function(coin, cb) {
var newHeavy = new Heavy({
2021-03-17 17:54:09 -06:00
coin: coin
2019-05-27 10:33:22 -07:00
});
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
newHeavy.save(function(err) {
if (err) {
console.log(err);
return cb();
} else {
2021-01-22 15:04:32 -07:00
console.log("initial heavycoin entry created for %s", coin);
2019-05-27 10:33:22 -07:00
return cb();
}
});
},
check_heavy: function(coin, cb) {
Heavy.findOne({coin: coin}, function(err, exists) {
2021-03-17 17:54:09 -06:00
if (exists)
2019-05-27 10:33:22 -07:00
return cb(true);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(false);
});
},
get_heavy: function(coin, cb) {
Heavy.findOne({coin: coin}, function(err, heavy) {
2021-03-17 17:54:09 -06:00
if (heavy)
2019-05-27 10:33:22 -07:00
return cb(heavy);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(null);
});
},
2021-01-22 15:04:32 -07:00
get_distribution: function(richlist, stats, cb) {
2019-05-27 10:33:22 -07:00
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 }
};
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
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;
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
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);
}
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
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);
}
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
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);
}
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
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);
}
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
loop.next();
2021-01-22 15:04:32 -07:00
}, 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 - (settings.richlist_page.burned_coins.include_burned_coins_in_distribution == true && richlist.burned > 0 ? ((richlist.burned / 100000000) / stats.supply) * 100 : 0)).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 - (settings.richlist_page.burned_coins.include_burned_coins_in_distribution == true && richlist.burned > 0 ? (richlist.burned / 100000000) : 0)).toFixed(8);
2019-05-27 10:33:22 -07:00
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);
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
return cb(distribution);
});
},
2020-12-31 15:19:48 -07:00
2021-01-22 15:04:32 -07:00
// updates heavycoin stats
2019-05-27 10:33:22 -07:00
// height: current block height, count: amount of votes to store
update_heavy: function(coin, height, count, cb) {
var newVotes = [];
2020-12-31 15:19:48 -07:00
2019-05-27 10:33:22 -07:00
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) {
2021-01-22 15:04:32 -07:00
module.exports.get_stats(settings.coin.name, function (stats) {
2019-05-27 10:33:22 -07:00
lib.get_estnext( function (estnext) {
lib.get_nextin( function (nextin) {
lib.syncLoop(count, function (loop) {
var i = loop.iteration();
2021-03-17 17:54:09 -06:00
2020-12-31 15:19:48 -07:00
lib.get_blockhash(height - i, function (hash) {
2019-05-27 10:33:22 -07:00
lib.get_block(hash, function (block) {
2020-12-31 15:19:48 -07:00
newVotes.push({ count: height - i, reward: block.reward, vote: (block && block.vote ? block.vote : 0) });
2019-05-27 10:33:22 -07:00
loop.next();
});
});
2020-12-31 15:19:48 -07:00
}, function() {
2019-06-07 20:05:28 -06:00
Heavy.updateOne({coin: coin}, {
lvote: (vote ? vote : 0),
reward: (reward ? reward : 0),
2021-01-22 15:04:32 -07:00
supply: (stats && stats.supply ? stats.supply : 0),
cap: (maxmoney ? maxmoney : 0),
estnext: (estnext ? estnext : 0),
phase: (phase ? phase : 'N/A'),
maxvote: (maxvote ? maxvote : 0),
nextin: (nextin ? nextin : 'N/A'),
2020-12-31 15:19:48 -07:00
votes: newVotes
2019-05-27 10:33:22 -07:00
}, function() {
2020-12-31 15:19:48 -07:00
// update reward_last_updated value
2021-01-22 15:04:32 -07:00
module.exports.update_last_updated_stats(settings.coin.name, { reward_last_updated: Math.floor(new Date() / 1000) }, function (new_cb) {
console.log('heavycoin update complete');
2020-12-31 15:19:48 -07:00
return cb();
});
2019-05-27 10:33:22 -07:00
});
});
});
});
});
});
});
});
});
});
},
// updates market data for given market; called by sync.js
2021-01-22 15:04:32 -07:00
update_markets_db: function(market, coin_symbol, pair_symbol, cb) {
2020-12-08 22:52:15 -07:00
// check if market exists
if (fs.existsSync('./lib/markets/' + market + '.js')) {
2021-01-22 15:04:32 -07:00
get_market_data(market, coin_symbol, pair_symbol, function (err, obj) {
// check if there was an error with getting market data
2020-12-08 22:52:15 -07:00
if (err == null) {
2021-01-22 15:04:32 -07:00
// update the market collection for the current market and trading pair combination
Markets.updateOne({market: market, coin_symbol: coin_symbol, pair_symbol: pair_symbol}, {
2020-12-08 22:52:15 -07:00
chartdata: JSON.stringify(obj.chartdata),
buys: obj.buys,
sells: obj.sells,
history: obj.trades,
summary: obj.stats
}, function() {
2021-01-24 15:56:45 -07:00
// check if this is the default market and trading pair and that the price is recorded in BTC
if (market == settings.markets_page.default_exchange.exchange_name && settings.markets_page.default_exchange.trading_pair.toUpperCase() == coin_symbol.toUpperCase() + '/' + pair_symbol.toUpperCase() && pair_symbol.toUpperCase() == 'BTC') {
2021-01-22 15:04:32 -07:00
// this is the default market so update the last price stats
Stats.updateOne({coin: settings.coin.name}, {
2021-03-17 17:54:09 -06:00
last_price: obj.stats.last
2021-01-22 15:04:32 -07:00
}, function() {
// finished updating market data
2020-12-08 22:52:15 -07:00
return cb(null);
});
} else {
2021-01-22 15:04:32 -07:00
// this is not the default market so we are finished updating market data
2019-05-27 10:33:22 -07:00
return cb(null);
2020-12-08 22:52:15 -07:00
}
});
} else {
2021-01-22 15:04:32 -07:00
// an error occurred with getting market data so return the error msg
2020-12-08 22:52:15 -07:00
return cb(err);
}
});
} else {
// market does not exist
return cb('market is not installed');
}
2019-05-27 10:33:22 -07:00
},
2021-01-22 15:04:32 -07:00
2019-05-27 10:33:22 -07:00
get_last_usd_price: function(cb) {
2021-01-22 15:04:32 -07:00
// check if the default market price is enabled and being recorded in BTC
if (settings.markets_page.exchanges[settings.markets_page.default_exchange.exchange_name].enabled == true && settings.markets_page.exchanges[settings.markets_page.default_exchange.exchange_name].trading_pairs.findIndex(p => p.toLowerCase().indexOf('/btc') > -1) > -1) {
// convert btc to usd via coindesk api
coindesk.get_data(function (err, last_usd) {
// get current stats
Stats.findOne({coin: settings.coin.name}, function(err, stats) {
// update the last usd price
Stats.updateOne({coin: settings.coin.name}, {
2021-03-17 17:54:09 -06:00
last_usd_price: (last_usd * stats.last_price)
2021-01-22 15:04:32 -07:00
}, function() {
return cb(null);
});
});
});
} else {
// only btc conversion supported so just exit without updating last price for now
return cb(null);
}
2019-05-27 10:33:22 -07:00
},
// updates stats data for given coin; called by sync.js
update_db: function(coin, cb) {
lib.get_blockcount( function (count) {
// check to ensure count is a positive number
if (!count || (count != null && typeof count === 'number' && count < 0)) {
2019-05-27 10:33:22 -07:00
console.log('Unable to connect to explorer API');
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
return cb(false);
}
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
lib.get_supply(function (supply) {
2019-05-27 10:33:22 -07:00
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) {
2021-03-17 17:54:09 -06:00
if (err)
console.log("Error during Stats Update: ", err);
2021-03-17 17:54:09 -06:00
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);
}
2019-05-27 10:33:22 -07:00
});
});
});
});
},
// updates tx, address & richlist db's; called by sync.js
update_tx_db: function(coin, start, end, txes, timeout, cb) {
2021-01-22 15:04:32 -07:00
var complete = false;
var blocks_to_scan = [];
var task_limit_blocks = settings.sync.block_parallel_tasks;
var task_limit_txs = 1;
2021-03-17 17:54:09 -06:00
// fix for invalid block height (skip genesis block as it should not have valid txs)
if (typeof start === 'undefined' || start < 1)
start = 1;
if (task_limit_blocks < 1)
task_limit_blocks = 1;
for (i = start; i < (end + 1); i++)
2021-01-22 15:04:32 -07:00
blocks_to_scan.push(i);
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
async.eachLimit(blocks_to_scan, task_limit_blocks, function(block_height, next_block) {
if (block_height % settings.sync.save_stats_after_sync_blocks === 0) {
Stats.updateOne({coin: coin}, {
last: block_height - 1,
txes: txes
}, function() {});
}
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
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) {
2021-03-17 17:54:09 -06:00
if (tx) {
2021-01-22 15:04:32 -07:00
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++;
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
setTimeout( function() {
tx = null;
next_tx();
}, timeout);
2019-05-27 10:33:22 -07:00
});
}
});
2021-01-22 15:04:32 -07:00
}, function() {
setTimeout( function() {
blockhash = null;
block = null;
next_block();
}, timeout);
2019-05-27 10:33:22 -07:00
});
2021-01-22 15:04:32 -07:00
} else {
console.log('block not found: %s', blockhash);
2021-03-17 17:54:09 -06:00
2021-01-22 15:04:32 -07:00
setTimeout( function() {
next_block();
}, timeout);
}
2019-05-27 10:33:22 -07:00
});
2021-01-22 15:04:32 -07:00
} else {
setTimeout( function() {
next_block();
}, timeout);
}
});
}, function() {
Stats.updateOne({coin: coin}, {
last: end,
txes: txes
}, function() {
return cb();
});
2019-05-27 10:33:22 -07:00
});
},
create_peer: function(params, cb) {
var newPeer = new Peers(params);
2021-03-17 17:54:09 -06:00
2019-05-27 10:33:22 -07:00
newPeer.save(function(err) {
if (err) {
console.log(err);
return cb();
2021-03-17 17:54:09 -06:00
} else
2019-05-27 10:33:22 -07:00
return cb();
});
},
find_peer: function(address, cb) {
Peers.findOne({address: address}, function(err, peer) {
2021-03-17 17:54:09 -06:00
if (err)
2019-05-27 10:33:22 -07:00
return cb(null);
2021-03-17 17:54:09 -06:00
else {
if (peer)
return cb(peer);
else
return cb (null)
2019-05-27 10:33:22 -07:00
}
2021-03-17 17:54:09 -06:00
});
2019-05-27 10:33:22 -07:00
},
drop_peer: function(address, cb) {
Peers.deleteOne({address: address}, function(err) {
if (err) {
console.log(err);
return cb();
2021-03-17 17:54:09 -06:00
} else
return cb();
});
},
drop_peers: function(cb) {
Peers.deleteMany({}, function(err) {
if (err) {
console.log(err);
return cb();
2021-03-17 17:54:09 -06:00
} else
return cb();
});
},
2019-05-27 10:33:22 -07:00
get_peers: function(cb) {
Peers.find({}, function(err, peers) {
2021-03-17 17:54:09 -06:00
if (err)
2019-05-27 10:33:22 -07:00
return cb([]);
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
return cb(peers);
});
2020-12-08 22:52:15 -07:00
},
2020-12-30 18:22:02 -07:00
// 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
2021-01-22 15:04:32 -07:00
if (settings.claim_address_page.enabled == true) {
2020-12-30 18:22:02 -07:00
// 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 = '';
}
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
// 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);
2021-03-17 17:54:09 -06:00
2020-12-30 18:22:02 -07:00
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);
});
},
2020-12-30 20:27:42 -07:00
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));
});
},
2020-12-30 18:22:02 -07:00
// updates last_updated stats; called by sync.js
update_last_updated_stats: function (coin, param, cb) {
2020-12-31 15:19:48 -07:00
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) {
2020-12-30 18:22:02 -07:00
// update masternode last updated date
Stats.updateOne({ coin: coin }, {
masternodes_last_updated: param.masternodes_last_updated
}, function () {
return cb(true);
});
2020-12-31 15:19:48 -07:00
} 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);
});
2020-12-30 18:22:02 -07:00
} else {
// invalid option
return cb(false);
}
},
2020-12-23 18:40:10 -07:00
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);
});
},
2020-12-08 22:52:15 -07:00
fs: fs
};