Detect RBF-transactions and offer to track them.

fixes #78
This commit is contained in:
softsimon
2020-06-08 18:55:53 +07:00
parent afe680b7cb
commit 87e9d631fc
11 changed files with 74 additions and 19 deletions

View File

@@ -25,4 +25,26 @@ export class Common {
arr.push(transactions[lastindex].feePerVsize);
return arr;
}
static findRbfTransactions(added: TransactionExtended[], deleted: TransactionExtended[]): { [txid: string]: TransactionExtended } {
const matches: { [txid: string]: TransactionExtended } = {};
deleted
// The replaced tx must have at least one input with nSequence < maxint-1 (Thats the opt-in)
.filter((tx) => tx.vin.some((vin) => vin.sequence < 0xfffffffe))
.forEach((deletedTx) => {
const foundMatches = added.find((addedTx) => {
// The new tx must, absolutely speaking, pay at least as much fee as the replaced tx.
return addedTx.fee > deletedTx.fee
// The new transaction must pay more fee per kB than the replaced tx.
&& addedTx.feePerVsize > deletedTx.feePerVsize
// Spends one or more of the same inputs
&& deletedTx.vin.some((deletedVin) =>
addedTx.vin.some((vin) => vin.txid === deletedVin.txid));
});
if (foundMatches) {
matches[deletedTx.txid] = foundMatches;
}
});
return matches;
}
}

View File

@@ -49,9 +49,9 @@ class MempoolBlocks {
transactions.push(tx);
} else {
mempoolBlocks.push(this.dataToMempoolBlocks(transactions, blockSize, blockVSize, mempoolBlocks.length));
blockVSize = 0;
blockSize = 0;
transactions = [];
blockVSize = tx.vsize;
blockSize = tx.size;
transactions = [tx];
}
});
if (transactions.length) {

View File

@@ -123,31 +123,30 @@ class Mempool {
}
}
// Replace mempool to clear already confirmed transactions
// Replace mempool to clear deleted transactions
const newMempool = {};
transactions.forEach((tx) => {
if (this.mempoolCache[tx]) {
const deletedTransactions: Transaction[] = [];
for (const tx in this.mempoolCache) {
if (transactions.indexOf(tx) > -1) {
newMempool[tx] = this.mempoolCache[tx];
} else {
hasChange = true;
deletedTransactions.push(this.mempoolCache[tx]);
}
});
}
if (!this.inSync && transactions.length === Object.keys(newMempool).length) {
this.inSync = true;
console.log('The mempool is now in sync!');
}
console.log(`New mempool size: ${Object.keys(newMempool).length} Change: ${diff}`);
this.mempoolCache = newMempool;
if (hasChange && this.mempoolChangedCallback) {
this.mempoolChangedCallback(this.mempoolCache, newTransactions);
if (this.mempoolChangedCallback && (hasChange || deletedTransactions.length)) {
this.mempoolCache = newMempool;
this.mempoolChangedCallback(this.mempoolCache, newTransactions, deletedTransactions);
}
const end = new Date().getTime();
const time = end - start;
console.log(`New mempool size: ${Object.keys(newMempool).length} Change: ${diff}`);
console.log('Mempool updated in ' + time / 1000 + ' seconds');
} catch (err) {
console.log('getRawMempool error.', err);

View File

@@ -8,6 +8,7 @@ import backendInfo from './backend-info';
import mempoolBlocks from './mempool-blocks';
import fiatConversion from './fiat-conversion';
import * as os from 'os';
import { Common } from './common';
class WebsocketHandler {
private wss: WebSocket.Server | undefined;
@@ -121,7 +122,8 @@ class WebsocketHandler {
});
}
handleMempoolChange(newMempool: { [txid: string]: TransactionExtended }, newTransactions: TransactionExtended[]) {
handleMempoolChange(newMempool: { [txid: string]: TransactionExtended },
newTransactions: TransactionExtended[], deletedTransactions: TransactionExtended[]) {
if (!this.wss) {
throw new Error('WebSocket.Server is not set');
}
@@ -130,6 +132,7 @@ class WebsocketHandler {
const mBlocks = mempoolBlocks.getMempoolBlocks();
const mempoolInfo = memPool.getMempoolInfo();
const vBytesPerSecond = memPool.getVBytesPerSecond();
const rbfTransactions = Common.findRbfTransactions(newTransactions, deletedTransactions);
this.wss.clients.forEach((client: WebSocket) => {
if (client.readyState !== WebSocket.OPEN) {
@@ -204,6 +207,15 @@ class WebsocketHandler {
}
}
if (client['track-tx'] && rbfTransactions[client['track-tx']]) {
for (const rbfTransaction in rbfTransactions) {
if (client['track-tx'] === rbfTransaction) {
response['rbfTransaction'] = rbfTransactions[rbfTransaction];
break;
}
}
}
if (Object.keys(response).length) {
client.send(JSON.stringify(response));
}
@@ -228,7 +240,7 @@ class WebsocketHandler {
}
}
matchRate = Math.ceil((matches.length / txIds.length) * 100);
matchRate = Math.round((matches.length / (txIds.length - 1)) * 100);
if (matchRate > 0) {
const currentMemPool = memPool.getMempool();
for (const txId of matches) {