2021-03-17 17:54:09 -06:00
var mongoose = require ( 'mongoose' ) ,
lib = require ( '../lib/explorer' ) ,
db = require ( '../lib/database' ) ,
Tx = require ( '../models/tx' ) ,
Address = require ( '../models/address' ) ,
AddressTx = require ( '../models/addresstx' ) ,
2023-05-06 12:36:35 -06:00
Orphans = require ( '../models/orphans' ) ,
2021-03-17 17:54:09 -06:00
Richlist = require ( '../models/richlist' ) ,
Stats = require ( '../models/stats' ) ,
2022-07-17 16:49:02 -06:00
settings = require ( '../lib/settings' ) ,
async = require ( 'async' ) ;
2019-05-27 10:33:22 -07:00
var mode = 'update' ;
var database = 'index' ;
2021-11-21 19:15:42 -07:00
var block _start = 1 ;
2022-04-30 20:53:10 -06:00
var lockCreated = false ;
2022-07-17 16:49:02 -06:00
var stopSync = false ;
// prevent stopping of the sync script to be able to gracefully shut down
process . on ( 'SIGINT' , ( ) => {
console . log ( 'Stopping sync process.. Please wait..' ) ;
stopSync = true ;
} ) ;
// prevent killing of the sync script to be able to gracefully shut down
process . on ( 'SIGTERM' , ( ) => {
console . log ( 'Stopping sync process.. Please wait..' ) ;
stopSync = true ;
} ) ;
2019-05-27 10:33:22 -07:00
// displays usage and exits
function usage ( ) {
2022-04-23 11:28:32 -06:00
console . log ( 'Usage: /path/to/node scripts/sync.js [mode]' ) ;
2019-05-27 10:33:22 -07:00
console . log ( '' ) ;
2020-12-04 10:59:58 -07:00
console . log ( 'Mode: (required)' ) ;
2020-12-03 14:16:34 -07:00
console . log ( 'update Updates index from last sync to current block' ) ;
console . log ( 'check Checks index for (and adds) any missing transactions/addresses' ) ;
2021-11-21 19:15:42 -07:00
console . log ( ' Optional parameter: block number to start checking from' ) ;
2020-12-03 14:16:34 -07:00
console . log ( 'reindex Clears index then resyncs from genesis to current block' ) ;
2021-03-17 17:54:09 -06:00
console . log ( 'reindex-rich Clears and recreates the richlist data' ) ;
2020-12-03 14:16:34 -07:00
console . log ( 'reindex-txcount Rescan and flatten the tx count value for faster access' ) ;
2021-01-24 15:26:17 -07:00
console . log ( 'reindex-last Rescan and flatten the last blockindex value for faster access' ) ;
2020-12-04 10:59:58 -07:00
console . log ( 'market Updates market summaries, orderbooks, trade history + charts' ) ;
console . log ( 'peers Updates peer info based on local wallet connections' ) ;
2020-12-30 18:22:02 -07:00
console . log ( 'masternodes Updates the list of active masternodes on the network' ) ;
2019-05-27 10:33:22 -07:00
console . log ( '' ) ;
2020-12-04 10:59:58 -07:00
console . log ( 'Notes:' ) ;
console . log ( '- \'current block\' is the latest created block when script is executed.' ) ;
console . log ( '- The market + peers databases only support (& defaults to) reindex mode.' ) ;
2021-11-21 13:22:24 -07:00
console . log ( '- If check mode finds missing data (other than new data since last sync),' ) ;
console . log ( ' this likely means that sync.update_timeout in settings.json is set too low.' ) ;
2019-05-27 10:33:22 -07:00
console . log ( '' ) ;
2022-04-30 20:53:10 -06:00
process . exit ( 100 ) ;
}
// exit function used to cleanup before finishing script
function exit ( exitCode ) {
// always disconnect mongo connection
mongoose . disconnect ( ) ;
// only remove sync lock if it was created in this session
if ( ! lockCreated || lib . remove _lock ( database ) == true ) {
// clean exit with previous exit code
process . exit ( exitCode ) ;
} else {
// error removing lock
process . exit ( 1 ) ;
}
}
2023-05-06 12:36:35 -06:00
// updates tx & address balances
2022-07-17 16:49:02 -06:00
function update _tx _db ( coin , start , end , txes , timeout , check _only , cb ) {
var complete = false ;
var blocks _to _scan = [ ] ;
var task _limit _blocks = settings . sync . block _parallel _tasks ;
var task _limit _txs = 1 ;
// 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 ++ )
blocks _to _scan . push ( i ) ;
async . eachLimit ( blocks _to _scan , task _limit _blocks , function ( block _height , next _block ) {
2023-05-06 12:36:35 -06:00
if ( check _only == 0 && block _height % settings . sync . save _stats _after _sync _blocks === 0 ) {
2022-07-17 16:49:02 -06:00
Stats . updateOne ( { coin : coin } , {
last : block _height - 1 ,
txes : txes
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => { } ) ;
2023-05-06 12:36:35 -06:00
} else if ( check _only == 1 ) {
2022-07-17 16:49:02 -06:00
console . log ( 'Checking block ' + block _height + '...' ) ;
}
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 ) {
2023-05-07 20:55:29 -06:00
Tx . findOne ( { txid : txid } ) . then ( ( tx ) => {
2023-05-06 12:36:35 -06:00
if ( tx && check _only != 2 ) {
setTimeout ( function ( ) {
2022-07-17 16:49:02 -06:00
tx = null ;
// check if the script is stopping
2023-05-06 12:36:35 -06:00
if ( stopSync && check _only != 2 ) {
2022-07-17 16:49:02 -06:00
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
} else {
2023-05-06 12:36:35 -06:00
// check if the transaction exists but doesn't match the current block height
check _delete _tx ( tx , block _height , txes , timeout , function ( updated _txes , tx _deleted ) {
// update the running tx count
txes = updated _txes ;
// check if this tx should be added to the local database
if ( tx _deleted || ! tx ) {
// save the transaction to local database
db . 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 ;
// check if the script is stopping
if ( stopSync && check _only != 2 ) {
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
} ) ;
} else {
// skip adding the current tx
setTimeout ( function ( ) {
tx = null ;
// check if the script is stopping
if ( stopSync && check _only != 2 ) {
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
}
2022-07-17 16:49:02 -06:00
} ) ;
}
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
setTimeout ( function ( ) {
tx = null ;
// check if the script is stopping
if ( stopSync && check _only != 2 ) {
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
2022-07-17 16:49:02 -06:00
} ) ;
} , function ( ) {
2023-05-06 12:36:35 -06:00
setTimeout ( function ( ) {
2022-07-17 16:49:02 -06:00
blockhash = null ;
block = null ;
// check if the script is stopping
2023-05-06 12:36:35 -06:00
if ( stopSync && check _only != 2 ) {
2022-07-17 16:49:02 -06:00
// stop the loop
next _block ( { } ) ;
} else
next _block ( ) ;
} , timeout ) ;
} ) ;
} else {
console . log ( 'Block not found: %s' , blockhash ) ;
2023-05-06 12:36:35 -06:00
setTimeout ( function ( ) {
2022-07-17 16:49:02 -06:00
// check if the script is stopping
2023-05-06 12:36:35 -06:00
if ( stopSync && check _only != 2 ) {
2022-07-17 16:49:02 -06:00
// stop the loop
next _block ( { } ) ;
} else
next _block ( ) ;
} , timeout ) ;
}
} ) ;
} else {
2023-05-06 12:36:35 -06:00
setTimeout ( function ( ) {
2022-07-17 16:49:02 -06:00
// check if the script is stopping
2023-05-06 12:36:35 -06:00
if ( stopSync && check _only != 2 ) {
2022-07-17 16:49:02 -06:00
// stop the loop
next _block ( { } ) ;
} else
next _block ( ) ;
} , timeout ) ;
}
} ) ;
} , function ( ) {
2023-05-06 12:36:35 -06:00
var statUpdateObject = { } ;
// check what stats data should be updated
if ( stopSync || check _only == 2 ) {
// only update txes when fixing invalid and missing blocks or when a "normal" sync was stopped prematurely
statUpdateObject . txes = txes ;
} else {
// update last and txes values for "normal" sync that finishes without being stopped prematurely
statUpdateObject = {
txes : txes ,
last : end
} ;
}
// update local stats
2023-05-07 20:55:29 -06:00
Stats . updateOne ( { coin : coin } , statUpdateObject ) . then ( ( ) => {
return cb ( txes ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
2023-05-06 12:36:35 -06:00
return cb ( txes ) ;
} ) ;
} ) ;
}
// fixes data belonging to orphaned blocks
function update _orphans ( orphan _index , orphan _current , last _blockindex , timeout , cb ) {
// lookup the earliest orphaned block if this is the first time that orphans are being checked
get _earliest _orphan _block ( orphan _index , orphan _current , last _blockindex , function ( orphan _data , err ) {
if ( err != null ) {
console . log ( err ) ;
return cb ( ) ;
} else {
// re-populate the orphan data in case it has changed
orphan _index = orphan _data . orphan _index ;
orphan _current = orphan _data . orphan _current ;
// Check if the sync msg should be shown
check _show _sync _message ( last _blockindex - orphan _index ) ;
// start from the current orphan (if exists) or else use the last orphan index
let current _block = ( orphan _current == 0 ? orphan _index : orphan _current ) ;
let unresolved _forks = [ ] ;
let correct _block _data = null ;
// loop infinitely until finished iterating through all known blocks
async . forever ( function ( next ) {
// check if the script is stopping or if the last block has been reached
if ( stopSync || current _block >= last _blockindex ) {
// stop the main loop
next ( 'stop' ) ;
} else {
if ( unresolved _forks . length == 0 && current _block % settings . sync . save _stats _after _sync _blocks === 0 ) {
Stats . updateOne ( { coin : settings . coin . name } , {
orphan _index : current _block - 1 ,
orphan _current : ( unresolved _forks . length == 0 ? 0 : unresolved _forks [ 0 ] )
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => { } ) . catch ( ( staterr ) => {
console . log ( staterr ) ;
} ) ;
2023-05-06 12:36:35 -06:00
}
// do not show the 'checking...' msg if this block is about to be resolved
if ( correct _block _data == null )
console . log ( current _block . toString ( ) + ': Checking for forks...' ) ;
// check if there is a fork in this block
check _block _height _for _fork ( current _block , function ( blockhashes , fork _err ) {
if ( fork _err != null ) {
// an error occurred
// stop looking for orphans
next ( fork _err ) ;
} else if ( blockhashes == null ) {
// no forks found in this block
// check if there are previously unresolved forks
if ( unresolved _forks . length == 0 ) {
// move to the next block
current _block ++ ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
} else {
// one or more forks still need to be resolved
// set the current block to the block after the next orphaned block
current _block = unresolved _forks [ unresolved _forks . length - 1 ] + 1 ;
// lookup the block hash using the block height
lib . get _blockhash ( current _block , function ( block _hash ) {
// check if the block hash was found
if ( block _hash ) {
// lookup the current block data from the wallet
lib . get _block ( block _hash , function ( block _data ) {
// remember the correct block hashes
correct _block _data = {
prev _hash : block _data . previousblockhash ,
next _hash : block _data . hash
} ;
console . log ( 'Good ' + current _block . toString ( ) + ' block found. Returning to fix block ' + unresolved _forks [ unresolved _forks . length - 1 ] . toString ( ) ) ;
// go back to the last unresolved fork
current _block = unresolved _forks . pop ( ) ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
} ) ;
} else {
// an error occurred
// stop looking for orphans
next ( 'cannot find block ' + current _block . toString ( ) ) ;
}
} ) ;
}
} else {
// there is at least one fork in this block height
// check if the correct block hash is known
if ( correct _block _data != null ) {
// this fork needs to be resolved
// lookup the current block data from the wallet
lib . get _block ( correct _block _data . prev _hash , function ( block _data ) {
var tx _count = 0 ;
// check if the good block hash is in the list of blockhashes
if ( blockhashes . indexOf ( correct _block _data . prev _hash ) > - 1 ) {
// remove the good block hash
blockhashes . splice ( blockhashes . indexOf ( correct _block _data . prev _hash ) , 1 ) ;
}
// loop through the remaining orphaned block hashes
lib . syncLoop ( blockhashes . length , function ( block _loop ) {
var i = block _loop . iteration ( ) ;
console . log ( 'Resolving orphaned block [' + ( i + 1 ) . toString ( ) + '/' + blockhashes . length . toString ( ) + ']: ' + blockhashes [ i ] ) ;
// find all orphaned txid's from the current orphan block hash
get _orphaned _txids ( blockhashes [ i ] , function ( txids ) {
// save the orphan block data to the orphan collection
create _orphan ( current _block , blockhashes [ i ] , correct _block _data . prev _hash , block _data . previousblockhash , correct _block _data . next _hash , function ( ) {
// loop through the remaining orphaned block hashes
lib . syncLoop ( txids . length , function ( tx _loop ) {
var t = tx _loop . iteration ( ) ;
// remove the orphaned tx and cleanup all associated data
delete _and _cleanup _tx ( txids [ t ] , current _block , tx _count , timeout , function ( updated _tx _count ) {
// update the running tx count
tx _count = updated _tx _count ;
// some blockchains will reuse the same orphaned transaction ids
// lookup the transaction that was just deleted to ensure it doesn't belong to another block
check _add _tx ( txids [ t ] , blockhashes [ i ] , tx _count , function ( updated _tx _count2 ) {
// update the running tx count
tx _count = updated _tx _count2 ;
setTimeout ( function ( ) {
// move to the next tx record
tx _loop . next ( ) ;
} , timeout ) ;
} ) ;
} ) ;
} , function ( ) {
setTimeout ( function ( ) {
// move to the next block record
block _loop . next ( ) ;
} , timeout ) ;
} ) ;
} ) ;
} ) ;
} , function ( ) {
// get the most recent stats
2023-05-07 20:55:29 -06:00
Stats . findOne ( { coin : settings . coin . name } ) . then ( ( stats ) => {
2023-05-06 12:36:35 -06:00
// add missing txes for the current block
update _tx _db ( settings . coin . name , current _block , current _block , ( stats . txes + tx _count ) , timeout , 2 , function ( updated _tx _count ) {
// update the stats collection by removing the orphaned txes in this block from the tx count
// and setting the orphan_index and orphan_current values in case the sync is interrupted before finishing
Stats . updateOne ( { coin : settings . coin . name } , {
orphan _index : current _block ,
orphan _current : ( unresolved _forks . length == 0 ? 0 : unresolved _forks [ 0 ] )
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => {
// clear the saved block hash data
correct _block _data = null ;
// move to the next block
current _block ++ ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
2023-05-06 12:36:35 -06:00
// clear the saved block hash data
correct _block _data = null ;
// move to the next block
current _block ++ ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
} ) ;
} ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
2023-05-06 12:36:35 -06:00
} ) ;
} ) ;
} ) ;
} else {
// correct block hash is unknown
console . log ( 'Block ' + current _block . toString ( ) + ' has ' + ( blockhashes . length - 1 ) . toString ( ) + ' unresolved fork' + ( blockhashes . length - 1 == 1 ? '' : 's' ) ) ;
// add this block height to the list of unresolved forks
unresolved _forks . push ( current _block ) ;
// move to the next block
current _block ++ ;
setTimeout ( function ( ) {
// process next block
next ( null ) ;
} , timeout ) ;
}
}
} ) ;
}
} ,
function ( err ) {
// check if there is a msg to display
if ( err != '' && err != 'stop' ) {
// display the msg
console . log ( err ) ;
// stop fixing orphaned block data
return cb ( ) ;
} else {
// check if the script is stopping
if ( ! stopSync )
console . log ( 'Finished looking for forks' ) ;
// get the list of orphans with a null next_blockhash
2023-05-07 20:55:29 -06:00
Orphans . find ( { next _blockhash : null } ) . exec ( ) . then ( ( orphans ) => {
2023-05-06 12:36:35 -06:00
// loop through the list of orphans
lib . syncLoop ( orphans . length , function ( orphan _loop ) {
var o = orphan _loop . iteration ( ) ;
// lookup the block data from the wallet
lib . get _block ( orphans [ o ] . good _blockhash , function ( good _block _data ) {
// check if the next block hash is known
if ( good _block _data . nextblockhash != null ) {
// update the next blockhash for this orphan record
Orphans . updateOne ( { blockindex : orphans [ o ] . blockindex , orphan _blockhash : orphans [ o ] . orphan _blockhash } , {
next _blockhash : good _block _data . nextblockhash
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => {
setTimeout ( function ( ) {
// move to the next orphan record
orphan _loop . next ( ) ;
} , timeout ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
2023-05-06 12:36:35 -06:00
setTimeout ( function ( ) {
// move to the next orphan record
orphan _loop . next ( ) ;
} , timeout ) ;
} ) ;
} else {
setTimeout ( function ( ) {
// move to the next orphan record
orphan _loop . next ( ) ;
} , timeout ) ;
}
} ) ;
} , function ( ) {
// update the orphan stats
Stats . updateOne ( { coin : settings . coin . name } , {
orphan _index : current _block - 1 ,
orphan _current : ( unresolved _forks . length == 0 ? 0 : unresolved _forks [ 0 ] )
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => {
2023-05-06 12:36:35 -06:00
// finished fixing orphaned block data
return cb ( ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( ) ;
2023-05-06 12:36:35 -06:00
} ) ;
} ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
} ) ;
}
} ) ;
}
function get _earliest _orphan _block ( orphan _index , orphan _current , last _blockindex , cb ) {
// check if it is necessary to search for orphan data
if ( orphan _index == null || orphan _index == 0 ) {
console . log ( 'Finding the earliest orphaned blockindex.. Please wait..' ) ;
Tx . aggregate ( [
{ $match : {
"vout" : { $exists : true , $ne : [ ] }
} } ,
{ $group : {
_id : "$blockindex" ,
blockhashes : { $addToSet : "$blockhash" }
} } ,
{ $match : {
"blockhashes.1" : { "$exists" : true }
} } ,
{ $sort : {
"_id" : 1
} } ,
{ $limit : 1 } ,
{ $project : {
"_id" : 1
} }
2023-05-07 20:55:29 -06:00
] ) . option ( { allowDiskUse : true } ) . then ( ( data ) => {
if ( data . length > 0 ) {
2023-05-06 12:36:35 -06:00
// found the first unprocessed orphaned block
orphan _current = data [ 0 ] . _id ;
orphan _index = ( orphan _current - 1 ) ;
console . log ( 'Found orphan block index: ' + orphan _current . toString ( ) ) ;
Stats . updateOne ( { coin : settings . coin . name } , {
orphan _current : orphan _current ,
orphan _index : orphan _index
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => {
2023-05-06 12:36:35 -06:00
return cb ( { orphan _index : orphan _index , orphan _current : orphan _current } , null ) ;
} ) ;
} else {
// no unprocessed orphaned blocks found
orphan _current = 0 ;
orphan _index = last _blockindex ;
console . log ( 'No orphaned blocks found' ) ;
Stats . updateOne ( { coin : settings . coin . name } , {
orphan _current : orphan _current ,
orphan _index : orphan _index
2023-05-07 20:55:29 -06:00
} ) . then ( ( ) => {
2023-05-06 12:36:35 -06:00
return cb ( { orphan _index : orphan _index , orphan _current : orphan _current } , null ) ;
} ) ;
}
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
return cb ( null , err ) ;
} ) ;
2023-05-06 12:36:35 -06:00
} else
return cb ( { orphan _index : orphan _index , orphan _current : orphan _current } , null ) ;
}
function check _block _height _for _fork ( block _height , cb ) {
// find all unique blockhashes in the txes collections for this block height
Tx . aggregate ( [
{ $match : {
$and : [
{ "blockindex" : { $eq : block _height } } ,
{ "vout" : { $exists : true , $ne : [ ] } }
]
} } ,
{ $group : {
_id : "$blockindex" ,
blockhashes : { $addToSet : "$blockhash" }
} }
2023-05-07 20:55:29 -06:00
] ) . then ( ( data ) => {
if ( data . length > 0 ) {
2023-05-06 12:36:35 -06:00
// lookup the "good" block hash using the block height
lib . get _blockhash ( block _height , function ( block _hash ) {
// check if there is more than 1 block hash
if ( data [ 0 ] . blockhashes . length == 1 ) {
// 1 block found
// check if the found block is the good block
if ( data [ 0 ] . blockhashes [ 0 ] == block _hash ) {
// no forks found
return cb ( null , null ) ;
} else {
// add the good block to the list of blockhashes
data [ 0 ] . blockhashes . push ( block _hash ) ;
// return the block hashes
return cb ( data [ 0 ] . blockhashes , null ) ;
}
} else {
// more than 1 block found
// check if the good block is already in the list
if ( data [ 0 ] . blockhashes . indexOf ( block _hash ) == - 1 ) {
// add the good block to the list of blockhashes
data [ 0 ] . blockhashes . push ( block _hash ) ;
}
// return the block hashes
return cb ( data [ 0 ] . blockhashes , null ) ;
}
2022-07-17 16:49:02 -06:00
} ) ;
2023-05-06 12:36:35 -06:00
} else {
// no blocks found
return cb ( null , null ) ;
}
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
// an error was returned
return cb ( null , err ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
function create _orphan ( blockindex , orphan _blockhash , good _blockhash , prev _blockhash , next _blockhash , cb ) {
var newOrphan = new Orphans ( {
blockindex : blockindex ,
orphan _blockhash : orphan _blockhash ,
good _blockhash : good _blockhash ,
prev _blockhash : prev _blockhash ,
next _blockhash : next _blockhash
} ) ;
// create a new orphan record in the local database
2023-05-07 20:55:29 -06:00
newOrphan . save ( ) . then ( ( ) => {
// new orphan record saved successfully
return cb ( ) ;
} ) . catch ( ( err ) => {
// check if this is a duplicate key error which can be ignored
if ( ! ( err . toString ( ) . indexOf ( 'E11000' ) > - 1 || err . toString ( ) . indexOf ( 'duplicate key error' ) > - 1 ) )
console . log ( err ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
return cb ( ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
function get _orphaned _txids ( block _hash , cb ) {
// get all transactions by block hash
2023-05-07 20:55:29 -06:00
Tx . find ( { blockhash : block _hash } ) . exec ( ) . then ( ( txes ) => {
if ( txes . length > 0 ) {
2023-05-06 12:36:35 -06:00
// found at least one orphaned transaction
var txids = [ ] ;
// populate an array of txids without the object data
for ( t = 0 ; t < txes . length ; t ++ )
txids . push ( txes [ t ] . txid ) ;
return cb ( txids , null ) ;
} else {
// no txes found
return cb ( null , null ) ;
}
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
// an error was returned
return cb ( null , err ) ;
2022-07-17 16:49:02 -06:00
} ) ;
}
2023-05-06 12:36:35 -06:00
function check _add _tx ( txid , blockhash , tx _count , cb ) {
// lookup the transaction to ensure it doesn't belong to another block
lib . get _rawtransaction ( txid , function ( tx ) {
// check if this txid belongs to the main blockchain
if ( tx && tx . txid && tx . blockhash != blockhash && tx . confirmations > 0 ) {
// lookup the correct block index in case it is not the same as the current block
lib . get _block ( tx . blockhash , function ( block ) {
// check if the block was found
if ( block ) {
// save the tx to the local database
db . save _tx ( txid , block . height , function ( save _tx _err , tx _has _vout ) {
// check if there were any save errors
if ( save _tx _err )
console . log ( save _tx _err ) ;
else
console . log ( '%s: %s' , block . height , txid ) ;
// check if the tx was saved correctly
if ( tx _has _vout ) {
// keep a running total of txes that were added
tx _count ++ ;
}
return cb ( tx _count ) ;
} ) ;
} else {
// block not found so there is nothing to fix
return cb ( tx _count ) ;
}
} ) ;
} else {
// block does not belong to main blockchain so there is nothing to fix
return cb ( tx _count ) ;
}
} ) ;
}
function delete _and _cleanup _tx ( txid , block _height , tx _count , timeout , cb ) {
// lookup all address tx records associated with the current tx
2023-05-07 20:55:29 -06:00
AddressTx . find ( { txid : txid } ) . exec ( ) . then ( ( address _txes ) => {
if ( address _txes . length == 0 ) {
2023-05-06 12:36:35 -06:00
// no vouts for this tx, so just delete the tx without cleaning up addresses
delete _tx ( txid , block _height , function ( tx _err , tx _result ) {
if ( tx _err ) {
console . log ( tx _err ) ;
return cb ( tx _count ) ;
} else {
// NOTE: do not subtract from the tx_count here because only txes with vouts are counted
return cb ( tx _count ) ;
}
} ) ;
} else {
// lookup the current tx in the local database
2023-05-07 20:55:29 -06:00
Tx . findOne ( { txid : txid } ) . then ( ( tx ) => {
var addressTxArray = [ ] ;
var has _vouts = ( tx . vout != null && tx . vout . length > 0 ) ;
// check if this is a coinbase tx
if ( tx . vin == null || tx . vin . length == 0 ) {
// add a coinbase tx into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : 'coinbase' ,
amount : tx . total
} ) ;
}
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// check if there are any vin addresses
if ( tx . vin != null && tx . vin . length > 0 ) {
// loop through the vin data
for ( var vin _tx _counter = tx . vin . length - 1 ; vin _tx _counter >= 0 ; vin _tx _counter -- ) {
// loop through the addresstxe data
for ( var vin _addresstx _counter = address _txes . length - 1 ; vin _addresstx _counter >= 0 ; vin _addresstx _counter -- ) {
// check if there is a tx record that exactly matches to the addresstx
if ( tx . vin [ vin _tx _counter ] . addresses == address _txes [ vin _addresstx _counter ] . a _id && tx . vin [ vin _tx _counter ] . amount == - address _txes [ vin _addresstx _counter ] . amount ) {
// add the address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : tx . vin [ vin _tx _counter ] . addresses ,
amount : address _txes [ vin _addresstx _counter ] . amount
} ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// remove the found records from both arrays
tx . vin . splice ( vin _tx _counter , 1 ) ;
address _txes . splice ( vin _addresstx _counter , 1 ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
break ;
2023-05-06 12:36:35 -06:00
}
}
}
2023-05-07 20:55:29 -06:00
}
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// check if there are any vout addresses
if ( tx . vout != null && tx . vout . length > 0 ) {
// loop through the vout data
for ( var vout _tx _counter = tx . vout . length - 1 ; vout _tx _counter >= 0 ; vout _tx _counter -- ) {
// loop through the addresstxe data
for ( var vout _addresstx _counter = address _txes . length - 1 ; vout _addresstx _counter >= 0 ; vout _addresstx _counter -- ) {
// check if there is a tx record that exactly matches to the addresstx
if ( tx . vout [ vout _tx _counter ] . addresses == address _txes [ vout _addresstx _counter ] . a _id && tx . vout [ vout _tx _counter ] . amount == address _txes [ vout _addresstx _counter ] . amount ) {
// add the address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : tx . vout [ vout _tx _counter ] . addresses ,
amount : address _txes [ vout _addresstx _counter ] . amount
} ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// remove the found records from both arrays
tx . vout . splice ( vout _tx _counter , 1 ) ;
address _txes . splice ( vout _addresstx _counter , 1 ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
break ;
2023-05-06 12:36:35 -06:00
}
}
}
2023-05-07 20:55:29 -06:00
}
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// check if there are still more vin/vout records to process
if ( tx . vin . length > 0 || tx . vout . length > 0 || address _txes . length > 0 ) {
// get all unique remaining addresses
var address _list = [ ] ;
// get unique addresses from the tx vin
tx . vin . forEach ( function ( vin ) {
if ( address _list . indexOf ( vin . addresses ) == - 1 )
address _list . push ( vin . addresses ) ;
} ) ;
// get unique addresses from the tx vout
tx . vout . forEach ( function ( vout ) {
if ( address _list . indexOf ( vout . addresses ) == - 1 )
address _list . push ( vout . addresses ) ;
} ) ;
// get unique addresses from the addresstxes
address _txes . forEach ( function ( address _tx ) {
if ( address _list . indexOf ( address _tx . a _id ) == - 1 )
address _list . push ( address _tx . a _id ) ;
} ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// loop through each unique address
address _list . forEach ( function ( address ) {
var vin _total = 0 ;
var vout _total = 0 ;
var address _tx _total = 0 ;
// add up all the vin amounts for this address
2023-05-06 12:36:35 -06:00
tx . vin . forEach ( function ( vin ) {
2023-05-07 20:55:29 -06:00
// check if this is the correct address
if ( vin . addresses == address )
vin _total += vin . amount ;
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
// add up all the vout amounts for this address
2023-05-06 12:36:35 -06:00
tx . vout . forEach ( function ( vout ) {
2023-05-07 20:55:29 -06:00
// check if this is the correct address
if ( vout . addresses == address )
vout _total += vout . amount ;
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
// add up all the addresstx amounts for this address
2023-05-06 12:36:35 -06:00
address _txes . forEach ( function ( address _tx ) {
2023-05-07 20:55:29 -06:00
// check if this is the correct address
if ( address _tx . a _id == address )
address _tx _total += address _tx . amount ;
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
// check if the tx and addresstx totals match
if ( ( vout _total - vin _total ) == address _tx _total ) {
// the values match (this indicates that this address sent coins to themselves)
// add a vin record for this address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : address ,
amount : - vin _total
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
// add a vout record for this address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : address ,
amount : vout _total
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
} else {
// the values do not match (this indicates there was a problem saving the data)
// output the data for this address as-is, using the addresstx values
2023-05-06 12:36:35 -06:00
address _txes . forEach ( function ( address _tx ) {
// check if this is the correct address
2023-05-07 20:55:29 -06:00
if ( address _tx . a _id == address ) {
// add a record for this address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : address ,
amount : address _tx . amount
} ) ;
}
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
}
} ) ;
}
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// loop through the address txes
lib . syncLoop ( addressTxArray . length , function ( address _loop ) {
var a = address _loop . iteration ( ) ;
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
// fix the balance, sent and received data for the current address
fix _address _data ( addressTxArray [ a ] , function ( ) {
setTimeout ( function ( ) {
// move to the next address record
address _loop . next ( ) ;
} , timeout ) ;
} ) ;
} , function ( ) {
// delete all AddressTx records from the local collection for this tx
AddressTx . deleteMany ( { txid : txid } ) . then ( ( address _tx _result ) => {
// delete the tx from the local database
delete _tx ( txid , block _height , function ( tx _err , tx _result ) {
if ( tx _err ) {
console . log ( tx _err ) ;
return cb ( tx _count ) ;
2023-05-06 12:36:35 -06:00
} else {
2023-05-07 20:55:29 -06:00
// check if the deleted tx had vouts
if ( has _vouts ) {
// keep a running total of txes that were removed
tx _count -= tx _result . deletedCount ;
}
2023-05-06 12:36:35 -06:00
2023-05-07 20:55:29 -06:00
return cb ( tx _count ) ;
}
2023-05-06 12:36:35 -06:00
} ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
// delete the tx from the local database
delete _tx ( txid , block _height , function ( tx _err , tx _result ) {
if ( tx _err ) {
console . log ( tx _err ) ;
return cb ( tx _count ) ;
} else {
// check if the deleted tx had vouts
if ( has _vouts ) {
// keep a running total of txes that were removed
tx _count -= tx _result . deletedCount ;
2023-05-06 12:36:35 -06:00
}
2023-05-07 20:55:29 -06:00
return cb ( tx _count ) ;
}
2023-05-06 12:36:35 -06:00
} ) ;
} ) ;
2023-05-07 20:55:29 -06:00
} ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( tx _count ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( tx _count ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
function fix _address _data ( address _data , cb ) {
var addr _inc = { } ;
var amount = address _data . amount ;
// determine how to fix the address balances
if ( address _data . a _id == 'coinbase' )
addr _inc . sent = - amount ;
else if ( amount < 0 ) {
// vin
addr _inc . sent = amount ;
addr _inc . balance = - amount ;
} else {
// vout
addr _inc . received = - amount ;
addr _inc . balance = - amount ;
}
// reverse the amount from the running totals in the Address collection for the current address
Address . findOneAndUpdate ( { a _id : address _data . a _id } , {
$inc : addr _inc
} , {
upsert : false
2023-05-07 20:55:29 -06:00
} ) . then ( ( return _address ) => {
2023-05-06 12:36:35 -06:00
// finished fixing the address balance data
return cb ( ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
function delete _tx ( txid , block _height , cb ) {
// delete the tx from the local database
2023-05-07 20:55:29 -06:00
Tx . deleteOne ( { txid : txid , blockindex : block _height } ) . then ( ( tx _result ) => {
return cb ( null , tx _result ) ;
} ) . catch ( ( err ) => {
return cb ( err , null ) ;
2023-05-06 12:36:35 -06:00
} ) ;
}
function check _delete _tx ( tx , block _height , tx _count , timeout , cb ) {
// check if the tx object exists and does not match the current block height
if ( tx && tx . blockindex != block _height ) {
// the transaction exists but does not match the correct block height, therefore it should be deleted
delete _and _cleanup _tx ( tx . txid , tx . blockindex , tx _count , timeout , function ( updated _tx _count ) {
// finished removing the transaction
return cb ( updated _tx _count , true ) ;
} ) ;
} else {
// tx dosn't exist or block heights match so nothing to do
return cb ( tx _count , false ) ;
}
}
2022-04-30 20:53:10 -06:00
function update _heavy ( coin , height , count , heavycoin _enabled , cb ) {
if ( heavycoin _enabled == true ) {
db . update _heavy ( coin , height , count , function ( ) {
return cb ( true ) ;
} ) ;
} else
return cb ( false ) ;
}
function update _network _history ( height , network _history _enabled , cb ) {
if ( network _history _enabled == true ) {
db . update _network _history ( height , function ( ) {
return cb ( true ) ;
} ) ;
} else
return cb ( false ) ;
}
function check _show _sync _message ( blocks _to _sync ) {
var retVal = false ;
var filePath = './tmp/show_sync_message.tmp' ;
// Check if the sync msg should be shown
if ( blocks _to _sync > settings . sync . show _sync _msg _when _syncing _more _than _blocks ) {
// Check if the show sync stub file already exists
if ( ! db . fs . existsSync ( filePath ) ) {
// File doesn't exist, so create it now
db . fs . writeFileSync ( filePath , '' ) ;
}
retVal = true ;
}
return retVal ;
}
2024-01-05 00:47:22 -07:00
function get _market _price ( market _array ) {
// check how the market price should be updated
if ( settings . markets _page . market _price == 'COINGECKO' ) {
// find the coingecko id
find _coingecko _id ( settings . coin . symbol , function ( coingecko _id ) {
// check if the coingecko_id was found
if ( coingecko _id != null && coingecko _id != '' ) {
const coingecko = require ( '../lib/apis/coingecko' ) ;
const currency = lib . get _market _currency _code ( ) ;
console . log ( 'Calculating market price.. Please wait..' ) ;
// get the market price from coingecko api
coingecko . get _market _prices ( coingecko _id , currency , function ( err , last _price , last _usd _price ) {
// check for errors
if ( err == null ) {
// get current stats
Stats . findOne ( { coin : settings . coin . name } ) . then ( ( stats ) => {
// update market stat prices
Stats . updateOne ( { coin : settings . coin . name } , {
last _price : ( last _price == null ? 0 : last _price ) ,
last _usd _price : ( last _usd _price == null ? 0 : last _usd _price )
} ) . then ( ( ) => {
// market prices updated successfully
finish _market _sync ( ) ;
} ) . catch ( ( err ) => {
// error saving stats
console . log ( err ) ;
exit ( 1 ) ;
} ) ;
} ) . catch ( ( err ) => {
// error getting stats
console . log ( err ) ;
exit ( 1 ) ;
} ) ;
} else {
// coingecko api returned an error
console . log ( err ) ;
exit ( 1 ) ;
}
} ) ;
} else {
// coingecko_id is not set which should have already thrown an error, so just exit
exit ( 1 ) ;
}
} ) ;
} else {
console . log ( 'Calculating market price.. Please wait..' ) ;
2023-10-27 18:33:52 -06:00
2024-01-05 00:47:22 -07:00
// get the list of coins from coingecko
coingecko _coin _list _api ( market _array , function ( coin _err , coin _list ) {
// check for errors
if ( coin _err == null ) {
let api _ids = '' ;
// loop through all unique currencies in the market_array
for ( let m = 0 ; m < market _array . length ; m ++ ) {
const index = coin _list . findIndex ( p => p . symbol . toLowerCase ( ) == market _array [ m ] . currency . toLowerCase ( ) ) ;
// check if the market currency is found in the coin list
if ( index > - 1 ) {
// add to the list of api_ids
api _ids += ( api _ids == '' ? '' : ',' ) + coin _list [ index ] . id ;
// add the coingecko id back to the market_array
market _array [ m ] . coingecko _id = coin _list [ index ] . id ;
} else {
// coin symbol not found in the api
console . log ( 'Error: Cannot find symbol "' + market _array [ m ] . currency + '" in the coingecko api' ) ;
}
}
// check if any api_ids were found
if ( api _ids != '' ) {
const coingecko = require ( '../lib/apis/coingecko' ) ;
const currency = lib . get _market _currency _code ( ) ;
// get the market price from coingecko api
coingecko . get _avg _market _prices ( api _ids , currency , market _array , function ( mkt _err , last _price , last _usd ) {
// check for errors
if ( mkt _err == null ) {
// update the last usd price
Stats . updateOne ( { coin : settings . coin . name } , {
last _price : last _price ,
last _usd _price : last _usd
} ) . then ( ( ) => {
// market price updated successfully
finish _market _sync ( ) ;
} ) . catch ( ( err ) => {
// error saving stat data
console . log ( err ) ;
exit ( 1 ) ;
} ) ;
} else {
// coingecko api returned an error
console . log ( mkt _err ) ;
exit ( 1 ) ;
}
} ) ;
2022-07-17 16:49:02 -06:00
} else {
2024-01-05 00:47:22 -07:00
// no api_ids found so cannot continue to getting the usd price and error msgs were already thrown, so just exit
exit ( 1 ) ;
2022-07-17 16:49:02 -06:00
}
2024-01-05 00:47:22 -07:00
} else {
// coingecko api returned an error
console . log ( coin _err ) ;
exit ( 1 ) ;
}
} ) ;
}
}
function finish _market _sync ( ) {
// update markets_last_updated value
db . update _last _updated _stats ( settings . coin . name , { markets _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( 'Market sync was stopped prematurely' ) ;
exit ( 1 ) ;
} else {
console . log ( 'Market sync complete' ) ;
exit ( 0 ) ;
}
} ) ;
}
function coingecko _coin _list _api ( market _symbols , cb ) {
let coin _array = [ ] ;
let call _coin _list _api = false ;
// check if market_symbols is an array
if ( ! Array . isArray ( market _symbols ) ) {
// add this symbol to an array
market _symbols = [ { currency : market _symbols } ] ;
}
// loop through all symbols
for ( var symbol of market _symbols ) {
// check if this symbol has a default coingecko id in the settings
const index = settings . default _coingecko _ids . findIndex ( p => p . symbol . toLowerCase ( ) == symbol . currency . toLowerCase ( ) ) ;
// check if the coin symbol is found in settings
if ( index > - 1 ) {
// add this symbol and id to a new array
coin _array . push ( {
id : settings . default _coingecko _ids [ index ] . id . toLowerCase ( ) ,
symbol : symbol . currency . toLowerCase ( )
2022-04-30 20:53:10 -06:00
} ) ;
} else {
2024-01-05 00:47:22 -07:00
// missing at least 1 symbol, so the coingecko api must be called
call _coin _list _api = true ;
break ;
}
}
// check if the coin list api needs to be called
if ( call _coin _list _api ) {
const coingecko = require ( '../lib/apis/coingecko' ) ;
// get the list of coins from coingecko
coingecko . get _coin _data ( function ( err , coin _list ) {
// check if there was an error
if ( err == null ) {
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
const rateLimitLib = require ( '../lib/ratelimit' ) ;
const rateLimit = new rateLimitLib . RateLimit ( 1 , 2000 , false ) ;
// automatically pause for 2 seconds in between requests
rateLimit . schedule ( function ( ) {
return cb ( err , coin _list ) ;
} ) ;
} else {
return cb ( err , coin _list ) ;
}
} ) ;
} else {
// return the custom array of known symbols and ids
return cb ( null , coin _array ) ;
}
}
function find _coingecko _id ( symbol , cb ) {
coingecko _coin _list _api ( symbol , function ( err , coin _list ) {
// check for errors
if ( err == null ) {
// find the index of the first coin symbol match
const index = coin _list . findIndex ( p => p . symbol . toLowerCase ( ) == symbol . toLowerCase ( ) ) ;
// check if the coin symbol is found in the api coin list
if ( index > - 1 )
return cb ( coin _list [ index ] . id ) ;
else {
// coin symbol not found in the api
console . log ( 'Error: Cannot find symbol "' + symbol + '" in the coingecko api' ) ;
return cb ( '' ) ;
}
} else {
// failed to get the coingecko api list
console . log ( err ) ;
return cb ( '' ) ;
2022-04-30 20:53:10 -06:00
}
} ) ;
}
/** Function that count occurrences of a substring in a string;
* @param {String} string The string
* @param {String} subString The sub string to search for
* @param {Boolean} [allowOverlapping] Optional. (Default:false)
*
* @author Vitim.us https://gist.github.com/victornpb/7736865
* @see Unit Test https://jsfiddle.net/Victornpb/5axuh96u/
* @see http://stackoverflow.com/questions/4009756/how-to-count-string-occurrence-in-string/7924240#7924240
*/
function occurrences ( string , subString , allowOverlapping ) {
string += "" ;
subString += "" ;
if ( subString . length <= 0 ) return ( string . length + 1 ) ;
var n = 0 ,
pos = 0 ,
step = allowOverlapping ? 1 : subString . length ;
while ( true ) {
pos = string . indexOf ( subString , pos ) ;
if ( pos >= 0 ) {
++ n ;
pos += step ;
} else break ;
}
return n ;
2019-05-27 10:33:22 -07:00
}
2023-09-23 17:39:50 -06:00
function block _sync ( reindex , stats ) {
// Get the last synced block index value
var last = ( stats . last ? stats . last : 0 ) ;
// Get the total number of blocks
var count = ( stats . count ? stats . count : 0 ) ;
// Check if the sync msg should be shown
check _show _sync _message ( count - last ) ;
update _tx _db ( settings . coin . name , last , count , stats . txes , settings . sync . update _timeout , 0 , function ( ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( ` ${ ( reindex ? 'Reindex' : 'Block sync' ) } was stopped prematurely ` ) ;
exit ( 1 ) ;
} else {
// update blockchain_last_updated value
db . update _last _updated _stats ( settings . coin . name , { blockchain _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( cb ) {
// fix data from orphaned blocks
update _orphans ( stats . orphan _index , stats . orphan _current , count , settings . sync . update _timeout , function ( ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( ` ${ ( reindex ? 'Reindex' : 'Block sync' ) } was stopped prematurely ` ) ;
exit ( 1 ) ;
} else {
db . update _richlist ( 'received' , function ( ) {
db . update _richlist ( 'balance' , function ( ) {
// update richlist_last_updated value
db . update _last _updated _stats ( settings . coin . name , { richlist _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( cb ) {
db . get _stats ( settings . coin . name , function ( nstats ) {
// check for and update heavycoin data if applicable
update _heavy ( settings . coin . name , count , 20 , settings . blockchain _specific . heavycoin . enabled , function ( heavy ) {
// check for and update network history data if applicable
update _network _history ( nstats . last , settings . network _history . enabled , function ( network _hist ) {
// always check for and remove the sync msg if exists
db . remove _sync _message ( ) ;
console . log ( ` ${ ( reindex ? 'Reindex' : 'Block sync' ) } complete (block: %s) ` , nstats . last ) ;
exit ( 0 ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
}
} ) ;
} ) ;
}
} ) ;
}
2019-05-27 10:33:22 -07:00
// check options
2022-04-23 11:28:32 -06:00
if ( process . argv [ 2 ] == null || process . argv [ 2 ] == 'index' || process . argv [ 2 ] == 'update' ) {
mode = null ;
switch ( process . argv [ 3 ] ) {
case undefined :
case null :
case 'update' :
mode = 'update' ;
break ;
case 'check' :
mode = 'check' ;
// check if the block start value was passed in and is an integer
if ( ! isNaN ( process . argv [ 4 ] ) && Number . isInteger ( parseFloat ( process . argv [ 4 ] ) ) ) {
// Check if the block start value is less than 1
if ( parseInt ( process . argv [ 4 ] ) < 1 )
block _start = 1 ;
else
block _start = parseInt ( process . argv [ 4 ] ) ;
}
break ;
case 'reindex' :
2023-09-23 17:39:50 -06:00
mode = 'reindex' ;
2022-04-23 11:28:32 -06:00
break ;
case 'reindex-rich' :
mode = 'reindex-rich' ;
break ;
case 'reindex-txcount' :
mode = 'reindex-txcount' ;
break ;
case 'reindex-last' :
mode = 'reindex-last' ;
break ;
default :
usage ( ) ;
}
2022-04-30 20:53:10 -06:00
} else if ( process . argv [ 2 ] == 'peers' || process . argv [ 2 ] == 'masternodes' )
database = process . argv [ 2 ] ;
else if ( process . argv [ 2 ] == 'market' )
database = ` ${ process . argv [ 2 ] } s ` ;
2021-03-17 17:54:09 -06:00
else
2019-05-27 10:33:22 -07:00
usage ( ) ;
2023-10-01 19:10:52 -06:00
// check if this sync option is already running/locked
if ( lib . is _locked ( [ database ] ) == false ) {
// create a new sync lock before checking the rest of the locks to minimize problems with running scripts at the same time
lib . create _lock ( database ) ;
// ensure the lock will be deleted on exit
lockCreated = true ;
// check the backup, restore and delete locks since those functions would be problematic when updating data
if ( lib . is _locked ( [ 'backup' , 'restore' , 'delete' ] ) == false ) {
// all tests passed. OK to run sync
console . log ( "Script launched with pid: " + process . pid ) ;
if ( mode == 'update' )
console . log ( ` Syncing ${ ( database == 'index' ? 'blocks' : database ) } .. Please wait.. ` ) ;
var dbString = 'mongodb://' + encodeURIComponent ( settings . dbsettings . user ) ;
dbString = dbString + ':' + encodeURIComponent ( settings . dbsettings . password ) ;
dbString = dbString + '@' + settings . dbsettings . address ;
dbString = dbString + ':' + settings . dbsettings . port ;
dbString = dbString + '/' + settings . dbsettings . database ;
mongoose . set ( 'strictQuery' , true ) ;
mongoose . connect ( dbString ) . then ( ( ) => {
if ( database == 'index' ) {
db . check _stats ( settings . coin . name , function ( exists ) {
if ( exists == false ) {
console . log ( 'Run \'npm start\' to create database structures before running this script.' ) ;
exit ( 1 ) ;
} else {
// determine which index mode to run
if ( mode == 'reindex' ) {
const { execSync } = require ( 'child_process' ) ;
try {
// delete the database
execSync ( ` node ./scripts/delete_database.js ${ mode } ` , { stdio : 'inherit' } ) ;
} catch ( err ) {
// delete_database.js was not successful, so exit
2023-09-23 17:39:50 -06:00
exit ( 1 ) ;
2023-10-01 19:10:52 -06:00
}
2022-04-30 20:53:10 -06:00
2023-10-01 19:10:52 -06:00
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
// start the block sync
block _sync ( true , stats ) ;
} else {
// update_db threw an error so exit
exit ( 1 ) ;
}
} ) ;
} else if ( mode == 'check' ) {
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
console . log ( 'Checking blocks.. Please wait..' ) ;
2022-04-30 20:53:10 -06:00
2023-10-01 19:10:52 -06:00
update _tx _db ( settings . coin . name , block _start , stats . count , stats . txes , settings . sync . check _timeout , 1 , function ( ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( 'Block check was stopped prematurely' ) ;
2023-09-23 17:39:50 -06:00
exit ( 1 ) ;
2022-07-17 16:49:02 -06:00
} else {
2023-10-01 19:10:52 -06:00
db . get _stats ( settings . coin . name , function ( nstats ) {
console . log ( 'Block check complete (block: %s)' , nstats . last ) ;
exit ( 0 ) ;
} ) ;
2022-07-17 16:49:02 -06:00
}
2022-04-30 20:53:10 -06:00
} ) ;
2023-10-01 19:10:52 -06:00
} else {
// update_db threw an error so exit
exit ( 1 ) ;
}
} ) ;
} else if ( mode == 'update' ) {
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
// start the block sync
block _sync ( false , stats ) ;
} else {
// update_db threw an error so exit
exit ( 1 ) ;
}
} ) ;
} else if ( mode == 'reindex-rich' ) {
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
console . log ( 'Check richlist' ) ;
db . check _richlist ( settings . coin . name , function ( exists ) {
if ( exists )
console . log ( 'Richlist entry found, deleting now..' ) ;
db . delete _richlist ( settings . coin . name , function ( deleted ) {
if ( deleted )
console . log ( 'Richlist entry deleted' ) ;
db . create _richlist ( settings . coin . name , false , function ( ) {
console . log ( 'Richlist created' ) ;
db . update _richlist ( 'received' , function ( ) {
console . log ( 'Richlist updated received' ) ;
db . update _richlist ( 'balance' , function ( ) {
// update richlist_last_updated value
db . update _last _updated _stats ( settings . coin . name , { richlist _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( cb ) {
console . log ( 'Richlist update complete' ) ;
exit ( 0 ) ;
2020-11-20 14:06:53 -07:00
} ) ;
} ) ;
} ) ;
2020-11-22 17:00:44 -07:00
} ) ;
2023-10-01 19:10:52 -06:00
} ) ;
} ) ;
} else {
// update_db threw an error so exit
exit ( 1 ) ;
}
} ) ;
} else if ( mode == 'reindex-txcount' ) {
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
console . log ( 'Calculating tx count.. Please wait..' ) ;
// Resetting the transaction counter requires a single lookup on the txes collection to find all txes that have a positive or zero total and 1 or more vout
Tx . find ( { 'total' : { $gte : 0 } , 'vout' : { $gte : { $size : 1 } } } ) . countDocuments ( ) . then ( ( count ) => {
console . log ( 'Found tx count: ' + count . toString ( ) ) ;
Stats . updateOne ( { coin : settings . coin . name } , {
txes : count
} ) . then ( ( ) => {
console . log ( 'Tx count update complete' ) ;
exit ( 0 ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
2023-05-07 20:55:29 -06:00
exit ( 1 ) ;
2023-10-01 19:10:52 -06:00
} ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
exit ( 1 ) ;
2022-04-30 20:53:10 -06:00
} ) ;
2023-10-01 19:10:52 -06:00
} else {
// update_db threw an error so exit
exit ( 1 ) ;
}
} ) ;
} else if ( mode == 'reindex-last' ) {
db . update _db ( settings . coin . name , function ( stats ) {
// check if stats returned properly
if ( stats !== false ) {
console . log ( 'Finding last blockindex.. Please wait..' ) ;
// Resetting the last blockindex counter requires a single lookup on the txes collection to find the last indexed blockindex
Tx . find ( { } , { blockindex : 1 , _id : 0 } ) . sort ( { blockindex : - 1 } ) . limit ( 1 ) . exec ( ) . then ( ( tx ) => {
// check if any blocks exists
if ( tx == null || tx . length == 0 ) {
console . log ( 'No blocks found. setting last blockindex to 0.' ) ;
Stats . updateOne ( { coin : settings . coin . name } , {
last : 0
} ) . then ( ( ) => {
console . log ( 'Last blockindex update complete' ) ;
exit ( 0 ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
exit ( 1 ) ;
2022-04-30 20:53:10 -06:00
} ) ;
} else {
2023-10-01 19:10:52 -06:00
console . log ( 'Found last blockindex: ' + tx [ 0 ] . blockindex . toString ( ) ) ;
Stats . updateOne ( { coin : settings . coin . name } , {
last : tx [ 0 ] . blockindex
} ) . then ( ( ) => {
console . log ( 'Last blockindex update complete' ) ;
exit ( 0 ) ;
2023-05-07 20:55:29 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
exit ( 1 ) ;
2021-02-03 20:54:15 -07:00
} ) ;
}
2023-10-01 19:10:52 -06:00
} ) . catch ( ( err ) => {
console . log ( err ) ;
exit ( 1 ) ;
2022-04-30 20:53:10 -06:00
} ) ;
2023-10-01 19:10:52 -06:00
} else {
// update_db threw an error so exit
exit ( 1 ) ;
2022-04-30 20:53:10 -06:00
}
2023-10-01 19:10:52 -06:00
} ) ;
}
}
} ) ;
} else if ( database == 'peers' ) {
lib . get _peerinfo ( function ( body ) {
if ( body != null ) {
lib . syncLoop ( body . length , function ( loop ) {
var i = loop . iteration ( ) ;
var address = body [ i ] . addr ;
var port = null ;
if ( occurrences ( address , ':' ) == 1 || occurrences ( address , ']:' ) == 1 ) {
// Separate the port # from the IP address
address = address . substring ( 0 , address . lastIndexOf ( ":" ) ) . replace ( "[" , "" ) . replace ( "]" , "" ) ;
port = body [ i ] . addr . substring ( body [ i ] . addr . lastIndexOf ( ":" ) + 1 ) ;
}
if ( address . indexOf ( "]" ) > - 1 ) {
// Remove [] characters from IPv6 addresses
address = address . replace ( "[" , "" ) . replace ( "]" , "" ) ;
2019-05-27 10:33:22 -07:00
}
2022-04-30 20:53:10 -06:00
2023-10-01 19:10:52 -06:00
db . find _peer ( address , port , function ( peer ) {
if ( peer ) {
if ( peer [ 'port' ] != null && ( isNaN ( peer [ 'port' ] ) || peer [ 'port' ] . length < 2 ) ) {
db . drop _peers ( function ( ) {
console . log ( 'Removing peers due to missing port information. Re-run this script to add peers again.' ) ;
exit ( 1 ) ;
} ) ;
2023-09-23 17:39:50 -06:00
}
2022-07-17 16:49:02 -06:00
2023-10-01 19:10:52 -06:00
// peer already exists and should be refreshed
// drop peer
db . drop _peer ( address , port , function ( ) {
// re-add the peer to refresh the data and extend the expiry date
db . create _peer ( {
address : address ,
port : port ,
protocol : peer . protocol ,
version : peer . version ,
country : peer . country ,
country _code : peer . country _code
} , function ( ) {
console . log ( 'Updated peer %s%s [%s/%s]' , address , ( port == null || port == '' ? '' : ':' + port . toString ( ) ) , ( i + 1 ) . toString ( ) , body . length . toString ( ) ) ;
// check if the script is stopping
if ( stopSync ) {
// stop the loop
loop . break ( true ) ;
2022-07-17 16:49:02 -06:00
}
2023-10-01 19:10:52 -06:00
loop . next ( ) ;
} ) ;
} ) ;
} else {
const rateLimitLib = require ( '../lib/ratelimit' ) ;
const rateLimit = new rateLimitLib . RateLimit ( 1 , 2000 , false ) ;
rateLimit . schedule ( function ( ) {
lib . get _geo _location ( address , function ( error , geo ) {
// check if an error was returned
if ( error ) {
console . log ( error ) ;
exit ( 1 ) ;
} else if ( geo == null || typeof geo != 'object' ) {
console . log ( 'Error: geolocation api did not return a valid object' ) ;
exit ( 1 ) ;
} else {
// add peer to collection
2022-04-30 20:53:10 -06:00
db . create _peer ( {
address : address ,
port : port ,
2023-10-01 19:10:52 -06:00
protocol : body [ i ] . version ,
version : body [ i ] . subver . replace ( '/' , '' ) . replace ( '/' , '' ) ,
country : geo . country _name ,
country _code : geo . country _code
2022-04-30 20:53:10 -06:00
} , function ( ) {
2023-10-01 19:10:52 -06:00
console . log ( 'Added new peer %s%s [%s/%s]' , address , ( port == null || port == '' ? '' : ':' + port . toString ( ) ) , ( i + 1 ) . toString ( ) , body . length . toString ( ) ) ;
2022-07-17 16:49:02 -06:00
// check if the script is stopping
if ( stopSync ) {
// stop the loop
loop . break ( true ) ;
}
2022-04-30 20:53:10 -06:00
loop . next ( ) ;
} ) ;
2023-10-01 19:10:52 -06:00
}
} ) ;
2023-09-23 17:39:50 -06:00
} ) ;
}
2023-10-01 19:10:52 -06:00
} ) ;
} , function ( ) {
// update network_last_updated value
db . update _last _updated _stats ( settings . coin . name , { network _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( cb ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( 'Peer sync was stopped prematurely' ) ;
exit ( 1 ) ;
} else {
console . log ( 'Peer sync complete' ) ;
exit ( 0 ) ;
}
} ) ;
} ) ;
} else {
console . log ( 'No peers found' ) ;
exit ( 2 ) ;
}
} ) ;
} else if ( database == 'masternodes' ) {
lib . get _masternodelist ( function ( body ) {
if ( body != null ) {
var isObject = false ;
var objectKeys = null ;
// Check if the masternode data is an array or an object
if ( body . length == null ) {
// Process data as an object
objectKeys = Object . keys ( body ) ;
isObject = true ;
}
2021-01-22 15:04:32 -07:00
2023-10-01 19:10:52 -06:00
lib . syncLoop ( ( isObject ? objectKeys : body ) . length , function ( loop ) {
var i = loop . iteration ( ) ;
2021-03-17 17:54:09 -06:00
2023-10-01 19:10:52 -06:00
db . save _masternode ( ( isObject ? body [ objectKeys [ i ] ] : body [ i ] ) , function ( success ) {
if ( success ) {
// check if the script is stopping
if ( stopSync ) {
// stop the loop
loop . break ( true ) ;
}
2022-07-17 16:49:02 -06:00
2023-10-01 19:10:52 -06:00
loop . next ( ) ;
} else {
console . log ( 'Error: Cannot save masternode %s.' , ( isObject ? ( body [ objectKeys [ i ] ] . payee ? body [ objectKeys [ i ] ] . payee : 'UNKNOWN' ) : ( body [ i ] . addr ? body [ i ] . addr : 'UNKNOWN' ) ) ) ;
exit ( 1 ) ;
}
} ) ;
} , function ( ) {
db . remove _old _masternodes ( function ( cb ) {
db . update _last _updated _stats ( settings . coin . name , { masternodes _last _updated : Math . floor ( new Date ( ) / 1000 ) } , function ( cb ) {
// check if the script stopped prematurely
if ( stopSync ) {
console . log ( 'Masternode sync was stopped prematurely' ) ;
exit ( 1 ) ;
} else {
console . log ( 'Masternode sync complete' ) ;
exit ( 0 ) ;
}
2020-12-08 22:52:15 -07:00
} ) ;
2023-10-01 19:10:52 -06:00
} ) ;
2022-04-30 20:53:10 -06:00
} ) ;
} else {
2023-10-01 19:10:52 -06:00
console . log ( 'No masternodes found' ) ;
exit ( 2 ) ;
}
} ) ;
} else {
2024-01-05 00:47:22 -07:00
// start market sync
// check if market feature is enabled or the market_price option is set to COINGECKO
if ( settings . markets _page . enabled == true || settings . markets _page . market _price == 'COINGECKO' ) {
2023-10-01 19:10:52 -06:00
var total _pairs = 0 ;
var exchanges = Object . keys ( settings . markets _page . exchanges ) ;
// loop through all exchanges to determine how many trading pairs must be updated
exchanges . forEach ( function ( key , index , map ) {
// check if market is enabled via settings
2024-01-05 00:47:22 -07:00
if ( settings . markets _page . enabled == true && settings . markets _page . exchanges [ key ] . enabled == true ) {
2023-10-01 19:10:52 -06:00
// check if market is installed/supported
if ( db . fs . existsSync ( './lib/markets/' + key + '.js' ) ) {
// add trading pairs to total
total _pairs += settings . markets _page . exchanges [ key ] . trading _pairs . length ;
// loop through all trading pairs for this market
for ( var i = 0 ; i < settings . markets _page . exchanges [ key ] . trading _pairs . length ; i ++ ) {
// ensure trading pair setting is always uppercase
settings . markets _page . exchanges [ key ] . trading _pairs [ i ] = settings . markets _page . exchanges [ key ] . trading _pairs [ i ] . toUpperCase ( ) ;
2022-04-30 20:53:10 -06:00
}
2023-10-01 19:10:52 -06:00
}
}
} ) ;
2019-06-07 20:05:28 -06:00
2023-10-01 19:10:52 -06:00
// check if there are any trading pairs to update
if ( total _pairs > 0 ) {
2024-01-05 00:47:22 -07:00
let market _array = [ ] ;
2023-10-01 19:10:52 -06:00
// initialize the rate limiter to wait 2 seconds between requests to prevent abusing external apis
var rateLimitLib = require ( '../lib/ratelimit' ) ;
var rateLimit = new rateLimitLib . RateLimit ( 1 , 2000 , false ) ;
2023-10-27 18:33:52 -06:00
var complete = 0 ;
2023-10-01 19:10:52 -06:00
// loop through and test all exchanges defined in the settings.json file
exchanges . forEach ( function ( key , index , map ) {
// check if market is enabled via settings
if ( settings . markets _page . exchanges [ key ] . enabled == true ) {
// check if market is installed/supported
if ( db . fs . existsSync ( './lib/markets/' + key + '.js' ) ) {
// loop through all trading pairs
settings . markets _page . exchanges [ key ] . trading _pairs . forEach ( function ( pair _key , pair _index , pair _map ) {
// split the pair data
var split _pair = pair _key . split ( '/' ) ;
// check if this is a valid trading pair
if ( split _pair . length == 2 ) {
// lookup the exchange in the market collection
db . check _market ( key , split _pair [ 0 ] , split _pair [ 1 ] , function ( mkt , exists ) {
// check if exchange trading pair exists in the market collection
if ( exists ) {
// automatically pause for 2 seconds in between requests
rateLimit . schedule ( function ( ) {
// update market data
2024-01-05 00:47:22 -07:00
db . update _markets _db ( key , split _pair [ 0 ] , split _pair [ 1 ] , function ( err , last _price ) {
if ( ! err ) {
2023-10-27 18:33:52 -06:00
console . log ( '%s[%s]: Market data updated successfully' , key , pair _key ) ;
2024-01-05 00:47:22 -07:00
// only add to the market_array if market data is being averaged
if ( settings . markets _page . market _price == 'AVERAGE' ) {
// check if the currency already exists in the market array
const index = market _array . findIndex ( item => item . currency . toUpperCase ( ) == split _pair [ 1 ] . toUpperCase ( ) ) ;
if ( index != - 1 ) {
// update the last_price
market _array [ index ] . last _price = ( market _array [ index ] . last _price + last _price ) / 2 ;
} else {
// add new object to the array
market _array . push ( { currency : split _pair [ 1 ] , last _price : last _price } ) ;
}
}
} else
2023-10-01 19:10:52 -06:00
console . log ( '%s[%s] Error: %s' , key , pair _key , err ) ;
2023-10-27 18:33:52 -06:00
complete ++ ;
if ( complete == total _pairs || stopSync )
2024-01-05 00:47:22 -07:00
get _market _price ( market _array ) ;
2023-10-01 19:10:52 -06:00
} ) ;
2022-04-30 20:53:10 -06:00
} ) ;
2023-10-01 19:10:52 -06:00
} else {
2023-10-27 18:33:52 -06:00
console . log ( '%s[%s] Error: Market not found in local database. Please restart the explorer' , key , pair _key ) ;
2023-10-01 19:10:52 -06:00
complete ++ ;
2023-10-27 18:33:52 -06:00
2023-10-01 19:10:52 -06:00
if ( complete == total _pairs || stopSync )
2024-01-05 00:47:22 -07:00
get _market _price ( market _array ) ;
2022-04-30 20:53:10 -06:00
}
} ) ;
2023-10-27 18:33:52 -06:00
} else {
// market pair not formatted correctly
console . log ( '%s market pair is invalid' , pair _key ) ;
complete ++ ;
if ( complete == total _pairs || stopSync )
2024-01-05 00:47:22 -07:00
get _market _price ( market _array ) ;
2023-09-23 17:39:50 -06:00
}
2023-10-01 19:10:52 -06:00
} ) ;
} else {
// market not installed
console . log ( '%s market not installed' , key ) ;
complete ++ ;
if ( complete == total _pairs || stopSync )
2024-01-05 00:47:22 -07:00
get _market _price ( market _array ) ;
2023-10-01 19:10:52 -06:00
}
2022-04-30 20:53:10 -06:00
}
2023-10-01 19:10:52 -06:00
} ) ;
2024-01-05 00:47:22 -07:00
} else if ( settings . markets _page . market _price == 'COINGECKO' )
get _market _price ( [ ] ) ;
else {
2023-10-01 19:10:52 -06:00
// no market trading pairs are enabled
console . log ( 'Error: No market trading pairs are enabled in settings' ) ;
exit ( 1 ) ;
2022-04-30 20:53:10 -06:00
}
2023-10-01 19:10:52 -06:00
} else {
// market page is not enabled
console . log ( 'Error: Market feature is disabled in settings' ) ;
2022-04-30 20:53:10 -06:00
exit ( 1 ) ;
2023-10-01 19:10:52 -06:00
}
2022-04-30 20:53:10 -06:00
}
2023-10-01 19:10:52 -06:00
} ) . catch ( ( err ) => {
console . log ( 'Error: Unable to connect to database: %s' , err ) ;
exit ( 1 ) ;
} ) ;
2022-04-30 20:53:10 -06:00
} else {
2023-10-01 19:10:52 -06:00
// another script process is currently running
console . log ( "Sync aborted" ) ;
2022-04-30 20:53:10 -06:00
exit ( 2 ) ;
2020-12-05 12:39:36 -07:00
}
2023-10-01 19:10:52 -06:00
} else {
// sync process is already running
console . log ( "Sync aborted" ) ;
exit ( 2 ) ;
}