From ea2a7e7505799f6e1122212cf935dda4ea3eeb1d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 02:10:38 +0000 Subject: [PATCH 1/5] Use sigops from mempool/electrs, fix the nodejs sigop calculation --- backend/src/api/bitcoin/esplora-api.interface.ts | 1 + backend/src/api/transaction-utils.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/backend/src/api/bitcoin/esplora-api.interface.ts b/backend/src/api/bitcoin/esplora-api.interface.ts index 55abe1d34..0a0960e46 100644 --- a/backend/src/api/bitcoin/esplora-api.interface.ts +++ b/backend/src/api/bitcoin/esplora-api.interface.ts @@ -6,6 +6,7 @@ export namespace IEsploraApi { size: number; weight: number; fee: number; + sigops?: number; vin: Vin[]; vout: Vout[]; status: Status; diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index ef4a34012..d4f130aa6 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -116,7 +116,10 @@ class TransactionUtils { public extendMempoolTransaction(transaction: IEsploraApi.Transaction): MempoolTransactionExtended { const vsize = Math.ceil(transaction.weight / 4); const fractionalVsize = (transaction.weight / 4); - const sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; + let sigops = transaction.sigops; + if (sigops == null) { + sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; + } // https://github.com/bitcoin/bitcoin/blob/e9262ea32a6e1d364fb7974844fadc36f931f8c6/src/policy/policy.cpp#L295-L298 const adjustedVsize = Math.max(fractionalVsize, sigops * 5); // adjusted vsize = Max(weight, sigops * bytes_per_sigop) / witness_scale_factor const feePerVbytes = (transaction.fee || 0) / fractionalVsize; @@ -155,7 +158,7 @@ class TransactionUtils { sigops += 20 * (script.match(/OP_CHECKMULTISIG/g)?.length || 0); } else { // in redeem scripts and witnesses, worth N if preceded by OP_N, 20 otherwise - const matches = script.matchAll(/(?:OP_(\d+))? OP_CHECKMULTISIG/g); + const matches = script.matchAll(/(?:OP_(?:PUSHNUM_)?(\d+))? OP_CHECKMULTISIG/g); for (const match of matches) { const n = parseInt(match[1]); if (Number.isInteger(n)) { @@ -189,6 +192,12 @@ class TransactionUtils { sigops += this.countScriptSigops(bitcoinjs.script.toASM(Buffer.from(input.witness[input.witness.length - 1], 'hex')), false, true); } break; + + case input.prevout.scriptpubkey_type === 'p2sh': + if (input.inner_redeemscript_asm) { + sigops += this.countScriptSigops(input.inner_redeemscript_asm); + } + break; } } } From 72e19f674ac77421d92569ffab7d0267d7edd7da Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 13:02:07 +0000 Subject: [PATCH 2/5] Clear Liquid sigops --- backend/src/api/transaction-utils.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/src/api/transaction-utils.ts b/backend/src/api/transaction-utils.ts index d4f130aa6..6ff1c10b7 100644 --- a/backend/src/api/transaction-utils.ts +++ b/backend/src/api/transaction-utils.ts @@ -116,10 +116,7 @@ class TransactionUtils { public extendMempoolTransaction(transaction: IEsploraApi.Transaction): MempoolTransactionExtended { const vsize = Math.ceil(transaction.weight / 4); const fractionalVsize = (transaction.weight / 4); - let sigops = transaction.sigops; - if (sigops == null) { - sigops = !Common.isLiquid() ? this.countSigops(transaction) : 0; - } + let sigops = Common.isLiquid() ? 0 : (transaction.sigops != null ? transaction.sigops : this.countSigops(transaction)); // https://github.com/bitcoin/bitcoin/blob/e9262ea32a6e1d364fb7974844fadc36f931f8c6/src/policy/policy.cpp#L295-L298 const adjustedVsize = Math.max(fractionalVsize, sigops * 5); // adjusted vsize = Max(weight, sigops * bytes_per_sigop) / witness_scale_factor const feePerVbytes = (transaction.fee || 0) / fractionalVsize; From a510b4992c14b8e0c086f422b9a4b641da34cfb3 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 16 Sep 2023 13:02:47 +0000 Subject: [PATCH 3/5] Fix condition to add mempool data to loaded cache txs --- backend/src/api/mempool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/mempool.ts b/backend/src/api/mempool.ts index 73260dc9e..f5e788f98 100644 --- a/backend/src/api/mempool.ts +++ b/backend/src/api/mempool.ts @@ -94,7 +94,7 @@ class Mempool { logger.debug(`Migrating ${Object.keys(this.mempoolCache).length} transactions from disk cache to Redis cache`); } for (const txid of Object.keys(this.mempoolCache)) { - if (!this.mempoolCache[txid].sigops || this.mempoolCache[txid].effectiveFeePerVsize == null) { + if (!this.mempoolCache[txid].adjustedVsize || this.mempoolCache[txid].sigops == null || this.mempoolCache[txid].effectiveFeePerVsize == null) { this.mempoolCache[txid] = transactionUtils.extendMempoolTransaction(this.mempoolCache[txid]); } if (this.mempoolCache[txid].order == null) { From 4ac0a6dad2556749c5542d1c359bc489eee3331a Mon Sep 17 00:00:00 2001 From: Mononaut Date: Tue, 19 Sep 2023 00:18:52 +0000 Subject: [PATCH 4/5] Display sigops on all transactions --- .../transaction/transaction.component.html | 8 ++++---- .../components/transaction/transaction.component.ts | 12 ++++++++++++ frontend/src/app/interfaces/electrs.interface.ts | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/components/transaction/transaction.component.html b/frontend/src/app/components/transaction/transaction.component.html index 006870864..f062662dc 100644 --- a/frontend/src/app/components/transaction/transaction.component.html +++ b/frontend/src/app/components/transaction/transaction.component.html @@ -292,9 +292,9 @@ Virtual size - + Adjusted vsize - + Weight @@ -314,9 +314,9 @@ Locktime - + Sigops - + Transaction hex diff --git a/frontend/src/app/components/transaction/transaction.component.ts b/frontend/src/app/components/transaction/transaction.component.ts index 4743e5cd6..5260cd668 100644 --- a/frontend/src/app/components/transaction/transaction.component.ts +++ b/frontend/src/app/components/transaction/transaction.component.ts @@ -62,6 +62,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { rbfReplaces: string[]; rbfInfo: RbfTree; cpfpInfo: CpfpInfo | null; + sigops: number | null; + adjustedVsize: number | null; showCpfpDetails = false; fetchCpfp$ = new Subject(); fetchRbfHistory$ = new Subject(); @@ -343,6 +345,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { if (tx.fee === undefined) { this.tx.fee = 0; } + if (this.tx.sigops != null) { + this.sigops = this.tx.sigops; + this.adjustedVsize = Math.max(this.tx.weight / 4, this.sigops * 5); + } this.tx.feePerVsize = tx.fee / (tx.weight / 4); this.isLoadingTx = false; this.error = undefined; @@ -543,6 +549,10 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { } this.cpfpInfo = cpfpInfo; + if (this.cpfpInfo.adjustedVsize && this.cpfpInfo.sigops != null) { + this.sigops = this.cpfpInfo.sigops; + this.adjustedVsize = this.cpfpInfo.adjustedVsize; + } this.hasEffectiveFeeRate = hasRelatives || (this.tx.effectiveFeePerVsize && (Math.abs(this.tx.effectiveFeePerVsize - this.tx.feePerVsize) > 0.01)); } @@ -569,6 +579,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy { this.replaced = false; this.transactionTime = -1; this.cpfpInfo = null; + this.adjustedVsize = null; + this.sigops = null; this.hasEffectiveFeeRate = false; this.rbfInfo = null; this.rbfReplaces = []; diff --git a/frontend/src/app/interfaces/electrs.interface.ts b/frontend/src/app/interfaces/electrs.interface.ts index 2d604a9de..58a02ad79 100644 --- a/frontend/src/app/interfaces/electrs.interface.ts +++ b/frontend/src/app/interfaces/electrs.interface.ts @@ -26,6 +26,7 @@ export interface Transaction { _outspends?: Outspend[]; _channels?: TransactionChannels; price?: Price; + sigops?: number; } export interface TransactionChannels { From 29299e622ec8cd7a96703900fcdbf2f6014b5b4d Mon Sep 17 00:00:00 2001 From: Mononaut Date: Mon, 13 Nov 2023 03:42:12 +0000 Subject: [PATCH 5/5] Calculate sigops in /api/v1/tx/:txid for mined txs --- backend/src/api/bitcoin/bitcoin.routes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/api/bitcoin/bitcoin.routes.ts b/backend/src/api/bitcoin/bitcoin.routes.ts index 240fb07ce..105b0be14 100644 --- a/backend/src/api/bitcoin/bitcoin.routes.ts +++ b/backend/src/api/bitcoin/bitcoin.routes.ts @@ -251,7 +251,7 @@ class BitcoinRoutes { private async getTransaction(req: Request, res: Response) { try { - const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true); + const transaction = await transactionUtils.$getTransactionExtended(req.params.txId, true, false, false, true); res.json(transaction); } catch (e) { let statusCode = 500;