2021-09-18 13:37:25 +04:00
import { IBitcoinApi } from '../bitcoin/bitcoin-api.interface' ;
import bitcoinClient from '../bitcoin/bitcoin-client' ;
import bitcoinSecondClient from '../bitcoin/bitcoin-second-client' ;
import { Common } from '../common' ;
import { DB } from '../../database' ;
import logger from '../../logger' ;
class ElementsParser {
2021-09-20 01:02:07 +04:00
private isRunning = false ;
2021-09-18 13:37:25 +04:00
constructor ( ) { }
public async $parse() {
if ( this . isRunning ) {
return ;
}
2021-09-20 01:02:07 +04:00
try {
this . isRunning = true ;
const result = await bitcoinClient . getChainTips ( ) ;
const tip = result [ 0 ] . height ;
2021-12-11 04:27:58 +04:00
const latestBlockHeight = await this . $getLatestBlockHeightFromDatabase ( ) ;
for ( let height = latestBlockHeight + 1 ; height <= tip ; height ++ ) {
2021-09-20 01:02:07 +04:00
const blockHash : IBitcoinApi.ChainTips = await bitcoinClient . getBlockHash ( height ) ;
const block : IBitcoinApi.Block = await bitcoinClient . getBlock ( blockHash , 2 ) ;
await this . $parseBlock ( block ) ;
2021-12-11 04:27:58 +04:00
await this . $saveLatestBlockToDatabase ( block . height ) ;
2021-09-20 01:02:07 +04:00
}
this . isRunning = false ;
} catch ( e ) {
this . isRunning = false ;
throw new Error ( e instanceof Error ? e . message : 'Error' ) ;
2021-09-18 13:37:25 +04:00
}
}
public async $getPegDataByMonth ( ) : Promise < any > {
2022-03-12 14:47:33 +01:00
const connection = await DB . getConnection ( ) ;
2021-09-18 13:37:25 +04:00
const query = ` SELECT SUM(amount) AS amount, DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y-%m-01') AS date FROM elements_pegs GROUP BY DATE_FORMAT(FROM_UNIXTIME(datetime), '%Y%m') ` ;
const [ rows ] = await connection . query < any > ( query ) ;
connection . release ( ) ;
return rows ;
}
protected async $parseBlock ( block : IBitcoinApi.Block ) {
for ( const tx of block . tx ) {
await this . $parseInputs ( tx , block ) ;
await this . $parseOutputs ( tx , block ) ;
}
}
protected async $parseInputs ( tx : IBitcoinApi.Transaction , block : IBitcoinApi.Block ) {
for ( const [ index , input ] of tx . vin . entries ( ) ) {
if ( input . is_pegin ) {
await this . $parsePegIn ( input , index , tx . txid , block ) ;
}
}
}
protected async $parsePegIn ( input : IBitcoinApi.Vin , vindex : number , txid : string , block : IBitcoinApi.Block ) {
const bitcoinTx : IBitcoinApi.Transaction = await bitcoinSecondClient . getRawTransaction ( input . txid , true ) ;
const prevout = bitcoinTx . vout [ input . vout || 0 ] ;
const outputAddress = prevout . scriptPubKey . address || ( prevout . scriptPubKey . addresses && prevout . scriptPubKey . addresses [ 0 ] ) || '' ;
await this . $savePegToDatabase ( block . height , block . time , prevout . value * 100000000 , txid , vindex ,
outputAddress , bitcoinTx . txid , prevout . n , 1 ) ;
}
protected async $parseOutputs ( tx : IBitcoinApi.Transaction , block : IBitcoinApi.Block ) {
for ( const output of tx . vout ) {
if ( output . scriptPubKey . pegout_chain ) {
await this . $savePegToDatabase ( block . height , block . time , 0 - output . value * 100000000 , tx . txid , output . n ,
( output . scriptPubKey . pegout_addresses && output . scriptPubKey . pegout_addresses [ 0 ] || '' ) , '' , 0 , 0 ) ;
}
if ( ! output . scriptPubKey . pegout_chain && output . scriptPubKey . type === 'nulldata'
&& output . value && output . value > 0 && output . asset && output . asset === Common . nativeAssetId ) {
await this . $savePegToDatabase ( block . height , block . time , 0 - output . value * 100000000 , tx . txid , output . n ,
( output . scriptPubKey . pegout_addresses && output . scriptPubKey . pegout_addresses [ 0 ] || '' ) , '' , 0 , 1 ) ;
}
}
}
protected async $savePegToDatabase ( height : number , blockTime : number , amount : number , txid : string ,
txindex : number , bitcoinaddress : string , bitcointxid : string , bitcoinindex : number , final_tx : number ) : Promise < void > {
2022-03-12 14:47:33 +01:00
const connection = await DB . getConnection ( ) ;
2021-09-18 13:37:25 +04:00
const query = ` INSERT INTO elements_pegs(
block , datetime , amount , txid , txindex , bitcoinaddress , bitcointxid , bitcoinindex , final_tx
) VALUES ( ? , ? , ? , ? , ? , ? , ? , ? , ? ) ` ;
const params : ( string | number ) [ ] = [
height , blockTime , amount , txid , txindex , bitcoinaddress , bitcointxid , bitcoinindex , final_tx
] ;
await connection . query ( query , params ) ;
connection . release ( ) ;
logger . debug ( ` Saved L-BTC peg from block height # ${ height } with TXID ${ txid } . ` ) ;
}
2021-12-11 04:27:58 +04:00
protected async $getLatestBlockHeightFromDatabase ( ) : Promise < number > {
2022-03-12 14:47:33 +01:00
const connection = await DB . getConnection ( ) ;
2021-12-11 04:27:58 +04:00
const query = ` SELECT number FROM state WHERE name = 'last_elements_block' ` ;
2021-09-18 13:37:25 +04:00
const [ rows ] = await connection . query < any > ( query ) ;
connection . release ( ) ;
2021-12-11 04:27:58 +04:00
return rows [ 0 ] [ 'number' ] ;
2021-09-18 13:37:25 +04:00
}
2021-12-11 04:27:58 +04:00
protected async $saveLatestBlockToDatabase ( blockHeight : number ) {
2022-03-12 14:47:33 +01:00
const connection = await DB . getConnection ( ) ;
2021-12-11 04:27:58 +04:00
const query = ` UPDATE state SET number = ? WHERE name = 'last_elements_block' ` ;
await connection . query < any > ( query , [ blockHeight ] ) ;
2021-09-18 13:37:25 +04:00
connection . release ( ) ;
}
}
export default new ElementsParser ( ) ;