2025-01-01 19:20:13 -07:00
const Stats = require ( '../models/stats' ) ;
const Tx = require ( '../models/tx' ) ;
const Address = require ( '../models/address' ) ;
const AddressTx = require ( '../models/addresstx' ) ;
const lib = require ( './explorer' ) ;
const settings = require ( '../lib/settings' ) ;
const async = require ( 'async' ) ;
let stopSync = false ;
2025-01-09 20:00:37 -07:00
let stackSizeErrorId = null ;
2025-01-01 19:20:13 -07: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
module . exports . 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 ) ;
}
}
function delete _tx ( txid , block _height , cb ) {
// delete the tx from the local database
Tx . deleteOne ( { txid : txid , blockindex : block _height } ) . then ( ( tx _result ) => {
return cb ( null , tx _result ) ;
} ) . catch ( ( err ) => {
return cb ( err , null ) ;
} ) ;
}
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
} ) . then ( ( return _address ) => {
// finished fixing the address balance data
return cb ( ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( ) ;
} ) ;
}
function hex _to _ascii ( hex ) {
let str = '' ;
for ( var i = 0 ; i < hex . length ; i += 2 )
str += String . fromCharCode ( parseInt ( hex . substr ( i , 2 ) , 16 ) ) ;
return str ;
}
function update _address ( hash , blockheight , txid , amount , type , cb ) {
let addr _inc = { }
if ( hash == 'coinbase' )
addr _inc . sent = amount ;
else {
if ( type == 'vin' ) {
addr _inc . sent = amount ;
addr _inc . balance = - amount ;
} else {
addr _inc . received = amount ;
addr _inc . balance = amount ;
}
}
Address . findOneAndUpdate ( { a _id : hash } , {
$inc : addr _inc
} , {
new : true ,
upsert : true
} ) . then ( ( address ) => {
if ( hash != 'coinbase' ) {
AddressTx . findOneAndUpdate ( { a _id : hash , txid : txid } , {
$inc : {
amount : addr _inc . balance
} ,
$set : {
a _id : hash ,
blockindex : blockheight ,
txid : txid
}
} , {
new : true ,
upsert : true
} ) . then ( ( addresstx ) => {
return cb ( ) ;
} ) . catch ( ( err ) => {
return cb ( err ) ;
} ) ;
} else
return cb ( ) ;
} ) . catch ( ( err ) => {
return cb ( err ) ;
} ) ;
}
2025-01-09 20:00:37 -07:00
function finalize _update _tx _db ( coin , check _only , end , txes , cb ) {
let statUpdateObject = { } ;
// check what stats data should be updated
if ( stopSync || stackSizeErrorId || 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
Stats . updateOne ( { coin : coin } , statUpdateObject ) . then ( ( ) => {
return cb ( ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( ) ;
} ) ;
}
2025-01-01 19:20:13 -07:00
module . exports = {
save _tx : function ( txid , blockheight , block , cb ) {
lib . get _rawtransaction ( txid , function ( tx ) {
if ( tx && tx != ` ${ settings . localization . ex _error } : ${ settings . localization . check _console } ` ) {
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 ) {
2025-01-09 20:00:37 -07:00
// check if vout is null which indicates an error
if ( vout != null ) {
lib . syncLoop ( nvin . length , function ( loop ) {
const i = loop . iteration ( ) ;
2025-01-01 19:20:13 -07:00
// check if address is inside an array
2025-01-09 20:00:37 -07:00
if ( Array . isArray ( nvin [ i ] . addresses ) ) {
2025-01-01 19:20:13 -07:00
// extract the address
2025-01-09 20:00:37 -07:00
nvin [ i ] . addresses = nvin [ i ] . addresses [ 0 ] ;
2025-01-01 19:20:13 -07:00
}
2025-01-09 20:00:37 -07:00
update _address ( nvin [ i ] . addresses , blockheight , txid , nvin [ i ] . amount , 'vin' , function ( ) {
loop . next ( ) ;
} ) ;
2025-01-01 19:20:13 -07:00
} , function ( ) {
2025-01-09 20:00:37 -07:00
lib . syncLoop ( vout . length , function ( subloop ) {
const t = subloop . iteration ( ) ;
2025-01-01 19:20:13 -07:00
2025-01-09 20:00:37 -07:00
// check if address is inside an array
if ( Array . isArray ( vout [ t ] . addresses ) ) {
// extract the address
vout [ t ] . addresses = vout [ t ] . addresses [ 0 ] ;
2025-01-01 19:20:13 -07:00
}
2025-01-09 20:00:37 -07:00
if ( vout [ t ] . addresses ) {
update _address ( vout [ t ] . addresses , blockheight , txid , vout [ t ] . amount , 'vout' , function ( ) {
subloop . next ( ) ;
} ) ;
} else
subloop . next ( ) ;
} , function ( ) {
lib . calculate _total ( vout , function ( total ) {
var op _return = null ;
var algo = null ;
// check if the op_return value should be decoded and saved
if ( settings . transaction _page . show _op _return ) {
// loop through vout to find the op_return value
tx . vout . forEach ( function ( vout _data ) {
// check if the op_return value exists
if ( vout _data . scriptPubKey != null && vout _data . scriptPubKey . asm != null && vout _data . scriptPubKey . asm . indexOf ( 'OP_RETURN' ) > - 1 ) {
// decode the op_return value
op _return = hex _to _ascii ( vout _data . scriptPubKey . asm . replace ( 'OP_RETURN' , '' ) . trim ( ) ) ;
}
} ) ;
}
// check if the algo value should be saved
if ( settings . block _page . multi _algorithm . show _algo ) {
// get the algo value
algo = block [ settings . block _page . multi _algorithm . key _name ] ;
}
const newTx = new Tx ( {
txid : tx . txid ,
vin : ( vin == null || vin . length == 0 ? [ ] : nvin ) ,
vout : vout ,
total : total . toFixed ( 8 ) ,
timestamp : tx . time ,
blockhash : tx . blockhash ,
blockindex : blockheight ,
tx _type : ( tx _type _vout == null ? tx _type _vin : tx _type _vout ) ,
op _return : op _return ,
algo : algo
} ) ;
2025-01-01 19:20:13 -07:00
2025-01-09 20:00:37 -07:00
newTx . save ( ) . then ( ( ) => {
return cb ( null , vout . length > 0 ) ;
} ) . catch ( ( err ) => {
return cb ( err , false ) ;
} ) ;
2025-01-01 19:20:13 -07:00
} ) ;
} ) ;
} ) ;
2025-01-09 20:00:37 -07:00
} else {
// create a custom error that will be specifically checked for later (NOTE: tx_type_vout contains the error code in this special case)
const customError = new Error ( tx _type _vout ) ;
customError . code = tx _type _vout ;
// return the custom error
return cb ( customError , false ) ;
}
2025-01-01 19:20:13 -07:00
} ) ;
} ) ;
} else
return cb ( 'tx not found: ' + txid , false ) ;
} ) ;
} ,
// updates tx & address balances
update _tx _db : function ( coin , start , end , txes , timeout , check _only , cb ) {
let blocks _to _scan = [ ] ;
let parallel _tasks = settings . sync . block _parallel _tasks ;
let last _block _height _to _save = 0 ;
// fix for invalid block height (skip genesis block as it should not have valid txs)
if ( typeof start === 'undefined' || start < 1 )
start = 1 ;
if ( parallel _tasks < 1 )
parallel _tasks = 1 ;
2025-01-09 20:00:37 -07:00
let finished _tasks = 0 ;
let processed _last _block = false ;
2025-01-01 19:20:13 -07:00
for ( i = start ; i < ( end + 1 ) ; i ++ )
blocks _to _scan . push ( i ) ;
// create an array to help keep track of all block numbers being processed at the same time
const block _numbers = Array ( parallel _tasks ) . fill ( 0 ) ;
// add a queue to manage access to the block array
const block _queue = async . queue ( ( task , cb ) => {
const { block _height , onComplete } = task ;
// select the first block number array index that is set to 0
const slotIndex = block _numbers . findIndex ( ( v ) => v === 0 ) ;
// wait for an available slot in the block array
if ( slotIndex === - 1 ) {
setTimeout ( ( ) => block _queue . push ( task , cb ) , 1 ) ; // retry after 1 ms
return ;
}
// assign the current block height to the slot
block _numbers [ slotIndex ] = block _height ;
// pass the slot index back
onComplete ( slotIndex ) ;
cb ( ) ;
} ) ;
async . eachLimit ( blocks _to _scan , parallel _tasks , function ( block _height , next _block ) {
2025-01-09 20:00:37 -07:00
// check if this is the last block to process
if ( block _height == end ) {
// ensure the process knows not to wait for more threads to stop after this
processed _last _block = true ;
}
2025-01-01 19:20:13 -07:00
// add the current block height to a queue and wait for it to be next in queue before starting to sync the block
block _queue . push (
{
block _height ,
onComplete : ( slotIndex ) => {
// check if it's time to save the last known block height to the database
if (
(
check _only == 0 &&
block _height % settings . sync . save _stats _after _sync _blocks === 0
) ||
(
last _block _height _to _save > 0 &&
block _numbers . every ( ( value ) => value >= last _block _height _to _save )
)
) {
// get the lowest block height currently being processed
const lowest _block _height = Math . min ( ... block _numbers . filter ( ( v ) => v !== 0 ) ) ;
// check if the current thread is processing the lowest block height
// or there was a previous block height that needs to be saved now that all threads have advanced beyond that saved height
if ( block _height == lowest _block _height || last _block _height _to _save > 0 ) {
// save the last known block height to the database along with the current tx count
Stats . updateOne ( { coin : coin } , {
last : ( last _block _height _to _save == 0 ? block _height : last _block _height _to _save ) ,
txes : txes
} ) . then ( ( ) => { } ) ;
// reset the "last block height to save" back to 0
last _block _height _to _save = 0 ;
} else if ( last _block _height _to _save == 0 ) {
// update the last known block height that should be saved
last _block _height _to _save = block _height ;
}
} else if ( check _only == 1 )
console . log ( 'Checking block ' + block _height + '...' ) ;
lib . get _blockhash ( block _height , function ( blockhash ) {
if ( blockhash ) {
lib . get _block ( blockhash , function ( block ) {
if ( block ) {
2025-01-09 20:00:37 -07:00
let tx _counter = 0 ;
// loop through all txes in this block
2025-01-01 19:20:13 -07:00
async . eachLimit ( block . tx , parallel _tasks , function ( txid , next _tx ) {
2025-01-09 20:00:37 -07:00
// increment tx counter
tx _counter ++ ;
2025-01-01 19:20:13 -07:00
Tx . findOne ( { txid : txid } ) . then ( ( tx ) => {
if ( tx && check _only != 2 ) {
setTimeout ( function ( ) {
tx = null ;
2025-01-09 20:00:37 -07:00
tx _counter -- ;
2025-01-01 19:20:13 -07:00
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
} else {
// 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
module . exports . save _tx ( txid , block _height , block , function ( err , tx _has _vout ) {
if ( err ) {
2025-01-09 20:00:37 -07:00
// check the error code
if ( err . code == 'StackSizeError' ) {
// ensure the process halts after stopping all sync threads
stackSizeErrorId = txid ;
} else if ( err . code === 11000 ) {
// output a nicer error msg for the 11000 error code "duplicate key error collection" which can happen in some blockchains with non-standard txids being reused
2025-01-01 19:20:13 -07:00
console . log ( ` ${ settings . localization . ex _warning } : ${ block _height } : ${ txid } already exists ` ) ;
2025-01-09 20:00:37 -07:00
} else
2025-01-01 19:20:13 -07:00
console . log ( err ) ;
}
else
console . log ( '%s: %s' , block _height , txid ) ;
if ( tx _has _vout )
txes ++ ;
setTimeout ( function ( ) {
tx = null ;
2025-01-09 20:00:37 -07:00
tx _counter -- ;
2025-01-01 19:20:13 -07:00
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
} ) ;
} else {
// skip adding the current tx
setTimeout ( function ( ) {
tx = null ;
2025-01-09 20:00:37 -07:00
tx _counter -- ;
2025-01-01 19:20:13 -07:00
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
}
} ) ;
}
} ) . catch ( ( err ) => {
console . log ( err ) ;
setTimeout ( function ( ) {
tx = null ;
2025-01-09 20:00:37 -07:00
tx _counter -- ;
2025-01-01 19:20:13 -07:00
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
next _tx ( { } ) ;
} else
next _tx ( ) ;
} , timeout ) ;
} ) ;
} , function ( ) {
2025-01-09 20:00:37 -07:00
// set the retry limit to a value that will be reached in ~10 seconds based on the
// timeout value which should be more than enough time for all threads to finish
// processing their last tx in case of error or cancel/kill script
const retryLimit = ( 10000 / timeout ) ;
let retryAttempts = 0 ;
// wait for all threads to finish before continuing
const handle = setInterval ( ( ) => {
// check if all threads have properly finished or else the retry limit has been reached
// NOTE: the retry limit should never need to be used but is put in place to prevent an
// infinite loop just in case something goes very wrong
if ( tx _counter === 0 || retryAttempts >= retryLimit ) {
// stop waiting for all threads to finish
clearInterval ( handle ) ;
blockhash = null ;
block = null ;
// reset the slot in the block array back to 0
block _numbers [ slotIndex ] = 0 ;
// check if the script is stopping
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
// stop the loop
finished _tasks ++ ;
next _block ( { } ) ;
} else {
// check if the last block is finished or in process and increment the finished counter
if ( processed _last _block )
finished _tasks ++ ;
// proceed to next block
next _block ( ) ;
}
}
2025-01-01 19:20:13 -07:00
} , timeout ) ;
} ) ;
} else {
console . log ( 'Block not found: %s' , blockhash ) ;
setTimeout ( function ( ) {
// reset the slot in the block array back to 0
block _numbers [ slotIndex ] = 0 ;
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
2025-01-09 20:00:37 -07:00
finished _tasks ++ ;
2025-01-01 19:20:13 -07:00
next _block ( { } ) ;
2025-01-09 20:00:37 -07:00
} else {
// check if the last block is finished or in process and increment the finished counter
if ( processed _last _block )
finished _tasks ++ ;
// proceed to next block
2025-01-01 19:20:13 -07:00
next _block ( ) ;
2025-01-09 20:00:37 -07:00
}
2025-01-01 19:20:13 -07:00
} , timeout ) ;
}
} ) ;
} else {
setTimeout ( function ( ) {
// reset the slot in the block array back to 0
block _numbers [ slotIndex ] = 0 ;
// check if the script is stopping
2025-01-09 20:00:37 -07:00
if ( ( stopSync && check _only != 2 ) || stackSizeErrorId ) {
2025-01-01 19:20:13 -07:00
// stop the loop
2025-01-09 20:00:37 -07:00
finished _tasks ++ ;
2025-01-01 19:20:13 -07:00
next _block ( { } ) ;
2025-01-09 20:00:37 -07:00
} else {
// check if the last block is finished or in process and increment the finished counter
if ( processed _last _block )
finished _tasks ++ ;
// proceed to next block
2025-01-01 19:20:13 -07:00
next _block ( ) ;
2025-01-09 20:00:37 -07:00
}
2025-01-01 19:20:13 -07:00
} , timeout ) ;
}
} ) ;
} ,
} ,
( ) => { }
) ;
} , function ( ) {
2025-01-09 20:00:37 -07:00
// set the retry limit to a value that will be reached in ~10 seconds based on the
// timeout value which should be more than enough time for all threads to finish
// processing their last tx in case of error or cancel/kill script
const retryLimit = ( 10000 / timeout ) ;
let retryAttempts = 0 ;
// wait for all threads to finish before continuing
const handle = setInterval ( ( ) => {
// check if all threads have properly finished or else the retry limit has been reached
// NOTE: the retry limit should never need to be used but is put in place to prevent an
// infinite loop just in case something goes very wrong
if ( finished _tasks === parallel _tasks || retryAttempts >= retryLimit ) {
// stop waiting for all threads to finish
clearInterval ( handle ) ;
// finish the update
finalize _update _tx _db ( coin , check _only , end , txes , function ( ) {
// check if the script should continue or respawn a new process
if ( ! stackSizeErrorId ) {
// continue to end of process
return cb ( txes ) ;
} else {
// reload the sync process
module . exports . respawnSync ( ) ;
}
} ) ;
} else {
// still waiting for threads to finish so increment the retry counter
retryAttempts ++ ;
}
} , timeout ) ;
2025-01-01 19:20:13 -07:00
} ) ;
} ,
delete _and _cleanup _tx : function ( txid , block _height , tx _count , timeout , cb ) {
// lookup all address tx records associated with the current tx
AddressTx . find ( { txid : txid } ) . exec ( ) . then ( ( address _txes ) => {
if ( address _txes . length == 0 ) {
// 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
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
} ) ;
}
// 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
} ) ;
// remove the found records from both arrays
tx . vin . splice ( vin _tx _counter , 1 ) ;
address _txes . splice ( vin _addresstx _counter , 1 ) ;
break ;
}
}
}
}
// 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
} ) ;
// remove the found records from both arrays
tx . vout . splice ( vout _tx _counter , 1 ) ;
address _txes . splice ( vout _addresstx _counter , 1 ) ;
break ;
}
}
}
}
// 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 ) ;
} ) ;
// 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
tx . vin . forEach ( function ( vin ) {
// check if this is the correct address
if ( vin . addresses == address )
vin _total += vin . amount ;
} ) ;
// add up all the vout amounts for this address
tx . vout . forEach ( function ( vout ) {
// check if this is the correct address
if ( vout . addresses == address )
vout _total += vout . amount ;
} ) ;
// add up all the addresstx amounts for this address
address _txes . forEach ( function ( address _tx ) {
// check if this is the correct address
if ( address _tx . a _id == address )
address _tx _total += address _tx . amount ;
} ) ;
// 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
} ) ;
// add a vout record for this address into the addressTxArray array
addressTxArray . push ( {
txid : txid ,
a _id : address ,
amount : vout _total
} ) ;
} 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
address _txes . forEach ( function ( address _tx ) {
// check if this is the correct address
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
} ) ;
}
} ) ;
}
} ) ;
}
// loop through the address txes
lib . syncLoop ( addressTxArray . length , function ( address _loop ) {
var a = address _loop . iteration ( ) ;
// 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 ) ;
} 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 ;
}
return cb ( tx _count ) ;
}
} ) ;
} ) . 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 ;
}
return cb ( tx _count ) ;
}
} ) ;
} ) ;
} ) ;
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( tx _count ) ;
} ) ;
}
} ) . catch ( ( err ) => {
console . log ( err ) ;
return cb ( tx _count ) ;
} ) ;
} ,
setStopSync : function ( value ) {
stopSync = value ;
} ,
getStopSync : function ( ) {
return stopSync ;
2025-01-09 20:00:37 -07:00
} ,
setStackSizeErrorId : function ( value ) {
stackSizeErrorId = value ;
} ,
getStackSizeErrorId : function ( ) {
return stackSizeErrorId ;
} ,
respawnSync : function ( ) {
let extraArgument = '' ;
// check if this is the benchmark script which must be handled slightly differently than a normal sync
if ( process . argv [ 1 ] . endsWith ( 'benchmark.js' ) ) {
// add the extra argument for benchmark syncing
extraArgument = '1' ;
}
const stackSizeArg = process . execArgv . find ( arg => arg . startsWith ( '--stack-size=' ) ) ;
let stackSize = 4096 ;
// check if the script was called with a stack size argument
if ( stackSizeArg ) {
// set the default stack size to the value that is currently being used
stackSize = parseInt ( stackSizeArg . split ( '=' ) [ 1 ] ) ;
}
// increase stack size by the elastic amount
stackSize += settings . sync . elastic _stack _size ;
// show an error msg
console . log ( ` ${ settings . localization . ex _error } : Maximum call stack size exceeded while processing txid ${ stackSizeErrorId } ` ) ;
console . log ( ` Restarting sync process with increased stack size of ${ stackSize } . ${ settings . localization . please _wait } .. ` ) ;
// filter out any existing --stack-size from execArgv
const execArgvWithoutStackSize = process . execArgv . filter ( arg => ! arg . startsWith ( '--stack-size=' ) ) ;
// populate child process arguments
const args = [
... execArgvWithoutStackSize ,
` --stack-size= ${ stackSize } ` ,
... process . argv . slice ( 1 ) // includes the path to the sync script and any user args
] ;
// add the extra argument to resume the benchmark sync and skip the unlock step
if ( extraArgument != '' )
args . push ( extraArgument ) ;
else {
// remove lock
lib . remove _lock ( process . argv [ 2 ] == null || process . argv [ 2 ] == '' ? 'index' : process . argv [ 2 ] ) ;
}
const { spawn } = require ( 'child_process' ) ;
// reload the sync process
const child = spawn ( process . execPath , args , {
stdio : 'inherit'
} ) ;
// when the child process ends, exit this parent process with the same code
child . on ( 'exit' , code => {
process . exit ( code ? ? 1 ) ;
} ) ;
2025-01-01 19:20:13 -07:00
}
} ;