106 lines
4.6 KiB
TypeScript
106 lines
4.6 KiB
TypeScript
|
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 {
|
||
|
isRunning = false;
|
||
|
constructor() { }
|
||
|
|
||
|
public async $parse() {
|
||
|
if (this.isRunning) {
|
||
|
return;
|
||
|
}
|
||
|
this.isRunning = true;
|
||
|
const result = await bitcoinClient.getChainTips();
|
||
|
const tip = result[0].height;
|
||
|
const latestBlock = await this.$getLatestBlockFromDatabase();
|
||
|
for (let height = latestBlock.block + 1; height <= tip; height++) {
|
||
|
const blockHash: IBitcoinApi.ChainTips = await bitcoinClient.getBlockHash(height);
|
||
|
const block: IBitcoinApi.Block = await bitcoinClient.getBlock(blockHash, 2);
|
||
|
await this.$parseBlock(block);
|
||
|
await this.$saveLatestBlockToDatabase(block.height, block.time, block.hash);
|
||
|
}
|
||
|
this.isRunning = false;
|
||
|
}
|
||
|
|
||
|
public async $getPegDataByMonth(): Promise<any> {
|
||
|
const connection = await DB.pool.getConnection();
|
||
|
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> {
|
||
|
const connection = await DB.pool.getConnection();
|
||
|
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}.`);
|
||
|
}
|
||
|
|
||
|
protected async $getLatestBlockFromDatabase(): Promise<any> {
|
||
|
const connection = await DB.pool.getConnection();
|
||
|
const query = `SELECT block, datetime, block_hash FROM last_elements_block`;
|
||
|
const [rows] = await connection.query<any>(query);
|
||
|
connection.release();
|
||
|
return rows[0];
|
||
|
}
|
||
|
|
||
|
protected async $saveLatestBlockToDatabase(blockHeight: number, datetime: number, blockHash: string) {
|
||
|
const connection = await DB.pool.getConnection();
|
||
|
const query = `UPDATE last_elements_block SET block = ?, datetime = ?, block_hash = ?`;
|
||
|
await connection.query<any>(query, [blockHeight, datetime, blockHash]);
|
||
|
connection.release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default new ElementsParser();
|