2022-05-19 16:40:38 +02:00
import { Common } from './api/common' ;
import blocks from './api/blocks' ;
import mempool from './api/mempool' ;
2022-07-11 19:15:28 +02:00
import mining from './api/mining/mining' ;
2022-05-19 16:40:38 +02:00
import logger from './logger' ;
2022-06-14 10:58:17 +02:00
import bitcoinClient from './api/bitcoin/bitcoin-client' ;
2022-07-11 23:16:48 +02:00
import priceUpdater from './tasks/price-updater' ;
2022-07-16 09:22:45 +02:00
import PricesRepository from './repositories/PricesRepository' ;
2022-05-19 16:40:38 +02:00
2023-02-17 21:21:21 +09:00
export interface CoreIndex {
name : string ;
synced : boolean ;
best_block_height : number ;
}
2022-05-19 16:40:38 +02:00
class Indexer {
runIndexer = true ;
indexerRunning = false ;
2022-07-16 09:22:45 +02:00
tasksRunning : string [ ] = [ ] ;
2023-02-17 21:21:21 +09:00
coreIndexes : CoreIndex [ ] = [ ] ;
/ * *
* Check which core index is available for indexing
* /
public async checkAvailableCoreIndexes ( ) : Promise < void > {
const updatedCoreIndexes : CoreIndex [ ] = [ ] ;
const indexes : any = await bitcoinClient . getIndexInfo ( ) ;
for ( const indexName in indexes ) {
const newState = {
name : indexName ,
synced : indexes [ indexName ] . synced ,
best_block_height : indexes [ indexName ] . best_block_height ,
} ;
logger . info ( ` Core index ' ${ indexName } ' is ${ indexes [ indexName ] . synced ? 'synced' : 'not synced' } . Best block height is ${ indexes [ indexName ] . best_block_height } ` ) ;
updatedCoreIndexes . push ( newState ) ;
if ( indexName === 'coinstatsindex' && newState . synced === true ) {
const previousState = this . isCoreIndexReady ( 'coinstatsindex' ) ;
// if (!previousState || previousState.synced === false) {
this . runSingleTask ( 'coinStatsIndex' ) ;
// }
}
}
this . coreIndexes = updatedCoreIndexes ;
}
2022-05-19 16:40:38 +02:00
2023-02-17 21:21:21 +09:00
/ * *
* Return the best block height if a core index is available , or 0 if not
*
* @param name
* @returns
* /
public isCoreIndexReady ( name : string ) : CoreIndex | null {
for ( const index of this . coreIndexes ) {
if ( index . name === name && index . synced === true ) {
return index ;
}
}
return null ;
}
public reindex ( ) : void {
2022-06-07 11:28:39 +02:00
if ( Common . indexingEnabled ( ) ) {
this . runIndexer = true ;
}
2022-05-19 16:40:38 +02:00
}
2023-02-17 21:21:21 +09:00
public async runSingleTask ( task : 'blocksPrices' | 'coinStatsIndex' ) : Promise < void > {
2022-07-16 09:22:45 +02:00
if ( ! Common . indexingEnabled ( ) ) {
return ;
}
if ( task === 'blocksPrices' && ! this . tasksRunning . includes ( task ) ) {
this . tasksRunning . push ( task ) ;
const lastestPriceId = await PricesRepository . $getLatestPriceId ( ) ;
if ( priceUpdater . historyInserted === false || lastestPriceId === null ) {
2023-02-25 11:46:52 +09:00
logger . debug ( ` Blocks prices indexer is waiting for the price updater to complete ` , logger . tags . mining ) ;
2022-07-16 09:22:45 +02:00
setTimeout ( ( ) = > {
2023-02-17 21:21:21 +09:00
this . tasksRunning = this . tasksRunning . filter ( runningTask = > runningTask !== task ) ;
2022-07-16 09:22:45 +02:00
this . runSingleTask ( 'blocksPrices' ) ;
} , 10000 ) ;
} else {
2023-02-25 11:46:52 +09:00
logger . debug ( ` Blocks prices indexer will run now ` , logger . tags . mining ) ;
2022-07-16 09:22:45 +02:00
await mining . $indexBlockPrices ( ) ;
2023-02-17 21:21:21 +09:00
this . tasksRunning = this . tasksRunning . filter ( runningTask = > runningTask !== task ) ;
2022-07-16 09:22:45 +02:00
}
}
2023-02-17 21:21:21 +09:00
if ( task === 'coinStatsIndex' && ! this . tasksRunning . includes ( task ) ) {
this . tasksRunning . push ( task ) ;
logger . debug ( ` Indexing coinStatsIndex now ` ) ;
await mining . $indexCoinStatsIndex ( ) ;
this . tasksRunning = this . tasksRunning . filter ( runningTask = > runningTask !== task ) ;
}
2022-07-16 09:22:45 +02:00
}
2023-02-17 21:21:21 +09:00
public async $run ( ) : Promise < void > {
2022-05-19 16:40:38 +02:00
if ( ! Common . indexingEnabled ( ) || this . runIndexer === false ||
this . indexerRunning === true || mempool . hasPriority ( )
) {
return ;
}
2022-06-14 10:58:17 +02:00
// Do not attempt to index anything unless Bitcoin Core is fully synced
const blockchainInfo = await bitcoinClient . getBlockchainInfo ( ) ;
if ( blockchainInfo . blocks !== blockchainInfo . headers ) {
return ;
}
2022-05-19 16:40:38 +02:00
this . runIndexer = false ;
this . indexerRunning = true ;
2023-02-25 11:46:52 +09:00
logger . debug ( ` Running mining indexer ` ) ;
2023-02-17 21:21:21 +09:00
await this . checkAvailableCoreIndexes ( ) ;
2022-07-09 18:25:16 +02:00
2022-05-19 16:40:38 +02:00
try {
2022-07-11 23:16:48 +02:00
await priceUpdater . $run ( ) ;
2022-06-20 16:35:10 +02:00
const chainValid = await blocks . $generateBlockDatabase ( ) ;
if ( chainValid === false ) {
// Chain of block hash was invalid, so we need to reindex. Stop here and continue at the next iteration
2023-02-25 11:46:52 +09:00
logger . warn ( ` The chain of block hash is invalid, re-indexing invalid data in 10 seconds. ` , logger . tags . mining ) ;
2022-06-25 19:50:39 +02:00
setTimeout ( ( ) = > this . reindex ( ) , 10000 ) ;
2022-06-20 16:35:10 +02:00
this . indexerRunning = false ;
return ;
}
2022-07-16 09:22:45 +02:00
this . runSingleTask ( 'blocksPrices' ) ;
2022-06-25 12:14:32 +02:00
await mining . $indexDifficultyAdjustments ( ) ;
2022-05-19 16:40:38 +02:00
await mining . $generateNetworkHashrateHistory ( ) ;
await mining . $generatePoolHashrateHistory ( ) ;
2022-06-18 16:48:02 +02:00
await blocks . $generateBlocksSummariesDatabase ( ) ;
2022-11-27 13:46:23 +09:00
await blocks . $generateCPFPDatabase ( ) ;
2023-06-09 13:46:43 -04:00
await blocks . $generateAuditStats ( ) ;
2022-05-19 16:40:38 +02:00
} catch ( e ) {
2022-06-25 19:50:39 +02:00
this . indexerRunning = false ;
logger . err ( ` Indexer failed, trying again in 10 seconds. Reason: ` + ( e instanceof Error ? e.message : e ) ) ;
setTimeout ( ( ) = > this . reindex ( ) , 10000 ) ;
2022-07-09 18:25:16 +02:00
this . indexerRunning = false ;
return ;
2022-05-19 16:40:38 +02:00
}
this . indexerRunning = false ;
2022-07-09 18:25:16 +02:00
const runEvery = 1000 * 3600 ; // 1 hour
logger . debug ( ` Indexing completed. Next run planned at ${ new Date ( new Date ( ) . getTime ( ) + runEvery ) . toUTCString ( ) } ` ) ;
setTimeout ( ( ) = > this . reindex ( ) , runEvery ) ;
2022-05-19 16:40:38 +02:00
}
}
export default new Indexer ( ) ;