2019-05-27 10:33:22 -07:00
var mongoose = require ( 'mongoose' )
2020-12-04 10:59:58 -07:00
, lib = require ( '../lib/explorer' )
2020-11-20 16:28:28 -07:00
, db = require ( '../lib/database' )
, Tx = require ( '../models/tx' )
, Address = require ( '../models/address' )
, AddressTx = require ( '../models/addresstx' )
, Richlist = require ( '../models/richlist' )
, Stats = require ( '../models/stats' )
, settings = require ( '../lib/settings' )
2020-12-08 22:52:15 -07:00
, request = require ( 'postman-request' ) ;
2019-05-27 10:33:22 -07:00
var mode = 'update' ;
var database = 'index' ;
// displays usage and exits
function usage ( ) {
2020-12-04 10:59:58 -07:00
console . log ( 'Usage: scripts/sync.sh /path/to/nodejs [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' ) ;
console . log ( 'reindex Clears index then resyncs from genesis to current block' ) ;
2020-12-04 10:59:58 -07: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' ) ;
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' ) ;
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.' ) ;
console . log ( '- If check mode finds missing data (ignoring new data since last sync),' ) ;
2019-05-27 10:33:22 -07:00
console . log ( ' index_timeout in settings.json is set too low.' )
console . log ( '' ) ;
process . exit ( 0 ) ;
}
// check options
if ( process . argv [ 2 ] == 'index' ) {
if ( process . argv . length < 3 ) {
usage ( ) ;
} else {
switch ( process . argv [ 3 ] )
{
2020-11-19 21:37:42 -07:00
case 'update' :
mode = 'update' ;
break ;
case 'check' :
mode = 'check' ;
break ;
case 'reindex' :
mode = 'reindex' ;
break ;
2020-11-20 14:06:53 -07:00
case 'reindex-rich' :
mode = 'reindex-rich' ;
2020-12-04 10:59:58 -07:00
break ;
2020-12-03 14:16:34 -07:00
case 'reindex-txcount' :
mode = 'reindex-txcount' ;
2020-11-20 14:06:53 -07:00
break ;
2020-11-19 21:37:42 -07:00
default :
usage ( ) ;
2019-05-27 10:33:22 -07:00
}
}
2020-12-04 10:59:58 -07:00
} else if ( process . argv [ 2 ] == 'market' ) {
2019-05-27 10:33:22 -07:00
database = 'market' ;
2020-12-04 10:59:58 -07:00
} else if ( process . argv [ 2 ] == 'peers' ) {
database = 'peers' ;
2019-05-27 10:33:22 -07:00
} else {
usage ( ) ;
}
function create _lock ( cb ) {
if ( database == 'index' ) {
var fname = './tmp/' + database + '.pid' ;
2020-12-08 22:52:15 -07:00
db . fs . appendFile ( fname , process . pid . toString ( ) , function ( err ) {
2019-05-27 10:33:22 -07:00
if ( err ) {
console . log ( "Error: unable to create %s" , fname ) ;
process . exit ( 1 ) ;
} else {
return cb ( ) ;
}
} ) ;
} else {
return cb ( ) ;
}
}
function remove _lock ( cb ) {
if ( database == 'index' ) {
var fname = './tmp/' + database + '.pid' ;
2020-12-08 22:52:15 -07:00
db . fs . unlink ( fname , function ( err ) {
2019-05-27 10:33:22 -07:00
if ( err ) {
console . log ( "unable to remove lock: %s" , fname ) ;
process . exit ( 1 ) ;
} else {
return cb ( ) ;
}
} ) ;
} else {
return cb ( ) ;
2020-11-19 21:37:42 -07:00
}
2019-05-27 10:33:22 -07:00
}
function is _locked ( cb ) {
if ( database == 'index' ) {
var fname = './tmp/' + database + '.pid' ;
2020-12-08 22:52:15 -07:00
db . fs . exists ( fname , function ( exists ) {
2019-05-27 10:33:22 -07:00
if ( exists ) {
return cb ( true ) ;
} else {
return cb ( false ) ;
}
} ) ;
} else {
return cb ( ) ;
2020-11-19 21:37:42 -07:00
}
2019-05-27 10:33:22 -07:00
}
function exit ( ) {
remove _lock ( function ( ) {
mongoose . disconnect ( ) ;
process . exit ( 0 ) ;
} ) ;
}
var dbString = 'mongodb://' + settings . dbsettings . user ;
dbString = dbString + ':' + settings . dbsettings . password ;
dbString = dbString + '@' + settings . dbsettings . address ;
dbString = dbString + ':' + settings . dbsettings . port ;
dbString = dbString + '/' + settings . dbsettings . database ;
2020-12-04 10:59:58 -07:00
if ( database == 'peers' ) {
console . log ( 'syncing peers.. please wait..' ) ;
// Initialize the rate limiting class from Matteo Agosti via https://www.matteoagosti.com/blog/2013/01/22/rate-limiting-function-calls-in-javascript/
var RateLimit = ( function ( ) {
var RateLimit = function ( maxOps , interval , allowBursts ) {
this . _maxRate = allowBursts ? maxOps : maxOps / interval ;
this . _interval = interval ;
this . _allowBursts = allowBursts ;
2020-11-19 21:37:42 -07:00
2020-12-04 10:59:58 -07:00
this . _numOps = 0 ;
this . _start = new Date ( ) . getTime ( ) ;
this . _queue = [ ] ;
} ;
RateLimit . prototype . schedule = function ( fn ) {
var that = this ,
rate = 0 ,
now = new Date ( ) . getTime ( ) ,
elapsed = now - this . _start ;
if ( elapsed > this . _interval ) {
this . _numOps = 0 ;
this . _start = now ;
}
rate = this . _numOps / ( this . _allowBursts ? 1 : elapsed ) ;
if ( rate < this . _maxRate ) {
if ( this . _queue . length === 0 ) {
this . _numOps ++ ;
fn ( ) ;
}
else {
if ( fn ) this . _queue . push ( fn ) ;
this . _numOps ++ ;
this . _queue . shift ( ) ( ) ;
}
}
else {
if ( fn ) this . _queue . push ( fn ) ;
setTimeout ( function ( ) {
that . schedule ( ) ;
} , 1 / this . _maxRate ) ;
}
} ;
return RateLimit ;
} ) ( ) ;
// syncing peers does not require a lock
mongoose . connect ( dbString , { useNewUrlParser : true , useCreateIndex : true , useUnifiedTopology : true , useFindAndModify : false } , function ( err ) {
if ( err ) {
console . log ( 'Unable to connect to database: %s' , dbString ) ;
console . log ( 'Aborting' ) ;
exit ( ) ;
} else {
request ( { uri : 'http://127.0.0.1:' + settings . port + '/api/getpeerinfo' , json : true , headers : { 'User-Agent' : 'eiquidus' } } , function ( error , response , body ) {
lib . syncLoop ( body . length , function ( loop ) {
var i = loop . iteration ( ) ;
var address = body [ i ] . addr . substring ( 0 , body [ i ] . addr . lastIndexOf ( ":" ) ) . replace ( "[" , "" ) . replace ( "]" , "" ) ;
var port = body [ i ] . addr . substring ( body [ i ] . addr . lastIndexOf ( ":" ) + 1 ) ;
var rateLimit = new RateLimit ( 1 , 2000 , false ) ;
db . find _peer ( address , function ( peer ) {
if ( peer ) {
if ( isNaN ( peer [ 'port' ] ) || peer [ 'port' ] . length < 2 || peer [ 'country' ] . length < 1 || peer [ 'country_code' ] . length < 1 ) {
db . drop _peers ( function ( ) {
console . log ( 'Saved peers missing ports or country, dropping peers. Re-run this script afterwards.' ) ;
exit ( ) ;
} ) ;
}
// peer already exists
loop . next ( ) ;
} else {
rateLimit . schedule ( function ( ) {
request ( { uri : 'https://freegeoip.app/json/' + address , json : true , headers : { 'User-Agent' : 'eiquidus' } } , function ( error , response , geo ) {
db . create _peer ( {
address : address ,
port : port ,
protocol : body [ i ] . version ,
version : body [ i ] . subver . replace ( '/' , '' ) . replace ( '/' , '' ) ,
country : geo . country _name ,
country _code : geo . country _code
} , function ( ) {
loop . next ( ) ;
} ) ;
} ) ;
} ) ;
}
} ) ;
} , function ( ) {
console . log ( 'peer sync complete' ) ;
exit ( ) ;
} ) ;
} ) ;
}
} ) ;
} else {
// index and market sync requires locking
is _locked ( function ( exists ) {
if ( exists ) {
console . log ( "Script already running.." ) ;
process . exit ( 0 ) ;
} else {
create _lock ( function ( ) {
console . log ( "script launched with pid: " + process . pid ) ;
mongoose . connect ( dbString , { useNewUrlParser : true , useCreateIndex : true , useUnifiedTopology : true , useFindAndModify : false } , function ( err ) {
if ( err ) {
console . log ( 'Unable to connect to database: %s' , dbString ) ;
console . log ( 'Aborting' ) ;
exit ( ) ;
} else if ( database == 'index' ) {
db . check _stats ( settings . coin , function ( exists ) {
if ( exists == false ) {
console . log ( 'Run \'npm start\' to create database structures before running this script.' ) ;
exit ( ) ;
} else {
db . update _db ( settings . coin , function ( stats ) {
if ( settings . heavy == true ) {
db . update _heavy ( settings . coin , stats . count , 20 , function ( ) {
} ) ;
}
if ( mode == 'reindex' ) {
Tx . deleteMany ( { } , function ( err ) {
console . log ( 'TXs cleared.' ) ;
Address . deleteMany ( { } , function ( err2 ) {
console . log ( 'Addresses cleared.' ) ;
AddressTx . deleteMany ( { } , function ( err3 ) {
console . log ( 'Address TXs cleared.' ) ;
Richlist . updateOne ( { coin : settings . coin } , {
received : [ ] ,
balance : [ ] ,
} , function ( err3 ) {
Stats . updateOne ( { coin : settings . coin } , {
last : 0 ,
count : 0 ,
supply : 0
} , function ( ) {
console . log ( 'index cleared (reindex)' ) ;
} ) ;
2020-12-05 12:39:36 -07:00
// Check if there are more than 1000 blocks to index
var showSync = check _show _sync _message ( stats . count ) ;
2020-12-04 10:59:58 -07:00
db . update _tx _db ( settings . coin , 1 , stats . count , stats . txes , settings . update _timeout , function ( ) {
db . update _richlist ( 'received' , function ( ) {
db . update _richlist ( 'balance' , function ( ) {
db . get _stats ( settings . coin , function ( nstats ) {
2020-12-05 12:39:36 -07:00
// Check if the sync msg was showing
if ( showSync ) {
// Remove the sync msg
remove _sync _message ( ) ;
}
2020-12-04 10:59:58 -07:00
console . log ( 'reindex complete (block: %s)' , nstats . last ) ;
exit ( ) ;
} ) ;
2019-05-27 10:33:22 -07:00
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
2020-11-19 21:37:42 -07:00
} ) ;
2020-12-04 10:59:58 -07:00
} else if ( mode == 'check' ) {
console . log ( 'starting check.. please wait..' ) ;
db . update _tx _db ( settings . coin , 1 , stats . count , stats . txes , settings . check _timeout , function ( ) {
db . get _stats ( settings . coin , function ( nstats ) {
console . log ( 'check complete (block: %s)' , nstats . last ) ;
exit ( ) ;
} ) ;
2019-05-27 10:33:22 -07:00
} ) ;
2020-12-04 10:59:58 -07:00
} else if ( mode == 'update' ) {
2020-12-08 20:51:35 -07:00
// 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 there are more than 1000 blocks to index
var showSync = check _show _sync _message ( count - last ) ;
2019-10-01 21:25:47 -06:00
2020-12-08 20:51:35 -07:00
db . update _tx _db ( settings . coin , last , count , stats . txes , settings . update _timeout , function ( ) {
db . update _richlist ( 'received' , function ( ) {
db . update _richlist ( 'balance' , function ( ) {
db . get _stats ( settings . coin , function ( nstats ) {
// Check if the sync msg was showing
if ( showSync ) {
// Remove the sync msg
remove _sync _message ( ) ;
}
console . log ( 'update complete (block: %s)' , nstats . last ) ;
exit ( ) ;
2019-05-27 10:33:22 -07:00
} ) ;
} ) ;
} ) ;
} ) ;
2020-12-04 10:59:58 -07:00
} else if ( mode == 'reindex-rich' ) {
console . log ( 'check richlist' ) ;
db . check _richlist ( settings . coin , function ( exists ) {
if ( exists ) console . log ( 'richlist entry found, deleting now..' ) ;
2020-11-22 17:00:44 -07:00
db . delete _richlist ( settings . coin , function ( deleted ) {
2020-12-04 10:59:58 -07:00
if ( deleted ) console . log ( 'richlist entry deleted' ) ;
2020-11-22 17:00:44 -07:00
db . create _richlist ( settings . coin , function ( ) {
console . log ( 'richlist created.' ) ;
2020-12-04 10:59:58 -07:00
db . update _richlist ( 'received' , function ( ) {
2020-11-22 17:00:44 -07:00
console . log ( 'richlist updated received.' ) ;
2020-12-04 10:59:58 -07:00
db . update _richlist ( 'balance' , function ( ) {
console . log ( 'richlist update complete' ) ;
exit ( ) ;
2020-11-20 14:06:53 -07:00
} ) ;
} ) ;
} ) ;
2020-11-22 17:00:44 -07:00
} ) ;
2020-12-03 14:16:34 -07:00
} ) ;
2020-12-04 10:59:58 -07:00
} else if ( mode == 'reindex-txcount' ) {
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 ( function ( err , count ) {
console . log ( 'found tx count: ' + count . toString ( ) ) ;
Stats . updateOne ( { coin : settings . coin } , {
txes : count
} , function ( ) {
console . log ( 'tx count update complete' ) ;
exit ( ) ;
} ) ;
} ) ;
2019-05-27 10:33:22 -07:00
}
} ) ;
}
} ) ;
2020-12-04 10:59:58 -07:00
} else {
//update markets
var markets = settings . markets . enabled ;
var complete = 0 ;
for ( var x = 0 ; x < markets . length ; x ++ ) {
2020-12-08 22:52:15 -07:00
// check if market is installed
if ( db . fs . existsSync ( './lib/markets/' + markets [ x ] + '.js' ) ) {
db . check _market ( markets [ x ] , function ( mkt , exists ) {
if ( exists ) {
db . update _markets _db ( mkt , function ( err ) {
if ( ! err ) {
console . log ( '%s market data updated successfully.' , mkt ) ;
complete ++ ;
if ( complete == markets . length )
get _last _usd _price ( ) ;
} else {
console . log ( '%s: %s' , mkt , err ) ;
complete ++ ;
if ( complete == markets . length )
get _last _usd _price ( ) ;
}
} ) ;
} else {
console . log ( 'error: entry for %s does not exists in markets db.' , mkt ) ;
complete ++ ;
if ( complete == markets . length )
get _last _usd _price ( ) ;
}
} ) ;
} else {
// market not installed
console . log ( '%s %s' , markets [ x ] , 'market not installed' ) ;
complete ++ ;
if ( complete == markets . length )
2020-12-04 10:59:58 -07:00
get _last _usd _price ( ) ;
2020-12-08 22:52:15 -07:00
}
2020-12-04 10:59:58 -07:00
}
2019-05-27 10:33:22 -07:00
}
2020-12-04 10:59:58 -07:00
} ) ;
2019-05-27 10:33:22 -07:00
} ) ;
2020-12-04 10:59:58 -07:00
}
} ) ;
}
2019-06-07 20:05:28 -06:00
2020-12-05 12:39:36 -07:00
function check _show _sync _message ( blocks _to _sync ) {
var retVal = false ;
2020-12-05 18:21:21 -07:00
var filePath = './tmp/show_sync_message.tmp' ;
2020-12-05 12:39:36 -07:00
// Check if there are more than 1000 blocks to index
if ( blocks _to _sync > 1000 ) {
// Check if the show sync stub file already exists
2020-12-08 22:52:15 -07:00
if ( ! db . fs . existsSync ( filePath ) ) {
2020-12-05 12:39:36 -07:00
// File doesn't exist, so create it now
2020-12-08 22:52:15 -07:00
db . fs . writeFileSync ( filePath , '' ) ;
2020-12-05 12:39:36 -07:00
}
retVal = true ;
}
return retVal ;
}
function remove _sync _message ( ) {
2020-12-05 18:21:21 -07:00
var filePath = './tmp/show_sync_message.tmp' ;
2020-12-05 12:39:36 -07:00
// Check if the show sync stub file exists
2020-12-08 22:52:15 -07:00
if ( db . fs . existsSync ( filePath ) ) {
2020-12-05 12:39:36 -07:00
// File exists, so delete it now
try {
2020-12-08 22:52:15 -07:00
db . fs . unlinkSync ( filePath ) ;
2020-12-05 12:39:36 -07:00
} catch ( err ) {
console . log ( err ) ;
}
}
}
2019-06-07 20:05:28 -06:00
function get _last _usd _price ( ) {
// Get the last usd price for coinstats
db . get _last _usd _price ( function ( retVal ) { exit ( ) ; } ) ;
}