@node-dlc/chainmon
Version:
Bitcoin on-chain transaction monitoring tool for DLCs
144 lines • 5.24 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BlockWatcher = void 0;
const bitcoin_1 = require("@node-dlc/bitcoin");
const bitcoinjs_lib_1 = require("bitcoinjs-lib");
const events_1 = require("events");
class BlockWatcher extends events_1.EventEmitter {
/**
* TxWatcher listens for transactions that match certain patterns
* and events when a transaction is found matching the pattern
*
* @param client
*/
constructor(client) {
super();
this._client = client;
this.watchedOutPoints = new Map();
this.watchedScriptPubKeys = new Map();
this.receivedBlocks = new Map();
this.previousHashToHash = new Map();
}
/**
* Starts watching for transactions
*/
start(latestBlock) {
this.receivedBlocks.set(latestBlock.hash, latestBlock);
this._client.subscribeRawBlock();
this._client.on('rawblock', this._onRawBlock.bind(this));
}
/**
* Stops watching for transactions
*/
stop() {
// this._client.close();
}
/**
* Watches an outpoint for broadcase in a new transaction
* @param outpoint
*/
watchOutpoint(outpoint) {
const key = outpoint.toString();
this.watchedOutPoints.set(key, outpoint);
}
/**
* Watches a scriptpubkey for broadcast in a new transaction
* @param scriptPubKey
* @param value
*/
watchScriptPubKey(scriptPubKey, value) {
const key = scriptPubKey.toString();
this.watchedScriptPubKeys.set(key, [scriptPubKey, value]);
}
////////////////////////////////////////////////////////////////
_checkOutpoints(block, tx) {
for (const vin of tx.inputs) {
const key = vin.outpoint.toString();
const watchedOutpoint = this.watchedOutPoints.get(key);
if (watchedOutpoint) {
this.watchedOutPoints.delete(key);
this.emit('outpointspent', block, tx, watchedOutpoint);
}
}
}
_checkScriptPubkeys(block, tx) {
for (const vout of tx.outputs) {
const key = vout.scriptPubKey.toString();
const watchedScriptPubKey = this.watchedScriptPubKeys.get(key);
if (watchedScriptPubKey) {
const [, value] = watchedScriptPubKey;
if (!value || (value && vout.value === value)) {
this.watchedScriptPubKeys.delete(key);
this.emit('scriptpubkeyreceived', block, tx, watchedScriptPubKey);
}
}
}
}
_onRawBlock(buf) {
const block = bitcoinjs_lib_1.Block.fromBuffer(buf);
let txs;
for (const transaction of block.transactions) {
if (!txs)
txs = [transaction.getId()];
}
const previousblockhash = Buffer.from(block.prevHash)
.reverse()
.toString('hex');
const previousBlock = this.receivedBlocks.get(previousblockhash);
const blockSize = buf.length;
if (previousBlock) {
this._processBlock(block, blockSize, txs, previousBlock);
}
else {
this._findPreviousBlockAndProcess(buf, previousblockhash);
}
}
_processBlock(block, blockSize, txs, previousBlock) {
const blockHash = block.getId();
const blockSummary = {
hash: blockHash,
confirmations: 1,
size: blockSize,
weight: block.weight(),
height: previousBlock.height + 1,
version: block.version,
versionHex: '',
merkleroot: block.merkleRoot.toString('hex'),
tx: txs,
time: block.timestamp,
mediantime: block.timestamp,
nonce: block.nonce,
bits: '',
difficulty: '',
chainwork: '',
nTx: block.transactions.length,
previousblockhash: previousBlock.hash,
nextblockhash: '',
};
this.receivedBlocks.set(blockSummary.hash, blockSummary);
for (const transaction of block.transactions) {
try {
const tx = bitcoin_1.Tx.fromBuffer(transaction.toBuffer());
this._checkOutpoints(blockSummary, tx);
this._checkScriptPubkeys(blockSummary, tx);
}
catch (e) {
console.error('Failed to deserialize tx', transaction.toBuffer().toString('hex'));
}
}
const existingBlockWithPrevHash = this.previousHashToHash.get(blockSummary.previousblockhash);
if (existingBlockWithPrevHash) {
this.emit('orphanblock', existingBlockWithPrevHash, blockSummary.hash);
}
else {
this.previousHashToHash.set(blockSummary.previousblockhash, blockSummary.hash);
}
}
async _findPreviousBlockAndProcess(currentBlockBuf, previousblockhash) {
const previousBuf = await this._client.getRawBlock(previousblockhash);
this._onRawBlock(previousBuf);
this._onRawBlock(currentBlockBuf);
}
}
exports.BlockWatcher = BlockWatcher;
//# sourceMappingURL=BlockWatcher.js.map