104 lines
4.4 KiB
TypeScript
104 lines
4.4 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 {
|
|
private isRunning = false;
|
|
|
|
constructor() { }
|
|
|
|
public async $parse() {
|
|
if (this.isRunning) {
|
|
return;
|
|
}
|
|
try {
|
|
this.isRunning = true;
|
|
const result = await bitcoinClient.getChainTips();
|
|
const tip = result[0].height;
|
|
const latestBlockHeight = await this.$getLatestBlockHeightFromDatabase();
|
|
for (let height = latestBlockHeight + 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);
|
|
}
|
|
this.isRunning = false;
|
|
} catch (e) {
|
|
this.isRunning = false;
|
|
throw new Error(e instanceof Error ? e.message : 'Error');
|
|
}
|
|
}
|
|
|
|
public async $getPegDataByMonth(): Promise<any> {
|
|
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 DB.query(query);
|
|
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 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 DB.query(query, params);
|
|
logger.debug(`Saved L-BTC peg from block height #${height} with TXID ${txid}.`);
|
|
}
|
|
|
|
protected async $getLatestBlockHeightFromDatabase(): Promise<number> {
|
|
const query = `SELECT number FROM state WHERE name = 'last_elements_block'`;
|
|
const [rows] = await DB.query(query);
|
|
return rows[0]['number'];
|
|
}
|
|
|
|
protected async $saveLatestBlockToDatabase(blockHeight: number) {
|
|
const query = `UPDATE state SET number = ? WHERE name = 'last_elements_block'`;
|
|
await DB.query(query, [blockHeight]);
|
|
}
|
|
}
|
|
|
|
export default new ElementsParser();
|