From 3ba37aaa5a43466c16c68f1bde57acf635fa6869 Mon Sep 17 00:00:00 2001 From: Antoni Spaanderman <56turtle56@gmail.com> Date: Tue, 26 Jul 2022 16:29:42 +0200 Subject: [PATCH] Address @Xekyo's comments on https://github.com/mempool/mempool/pull/2167 + minor fixes, variable name consistency and ts types --- frontend/src/app/bitcoin.utils.ts | 68 ++++++++++--------- .../tx-features/tx-features.component.html | 10 +-- .../tx-features/tx-features.component.ts | 6 +- 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/frontend/src/app/bitcoin.utils.ts b/frontend/src/app/bitcoin.utils.ts index b2ea1d7a9..25432565c 100644 --- a/frontend/src/app/bitcoin.utils.ts +++ b/frontend/src/app/bitcoin.utils.ts @@ -5,9 +5,9 @@ const P2SH_P2WSH_COST = 35 * 4; // the WU cost for the non-witness part of P2SH export function calcSegwitFeeGains(tx: Transaction) { // calculated in weight units - let realizedBech32Gains = 0; - let potentialBech32Gains = 0; - let potentialP2shGains = 0; + let realizedSegwitGains = 0; + let potentialSegwitGains = 0; + let potentialP2shSegwitGains = 0; let potentialTaprootGains = 0; let realizedTaprootGains = 0; @@ -24,31 +24,33 @@ export function calcSegwitFeeGains(tx: Transaction) { const isP2tr = vin.prevout.scriptpubkey_type === 'v1_p2tr'; const op = vin.scriptsig ? vin.scriptsig_asm.split(' ')[0] : null; - const isP2sh2Wpkh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_22'; - const isP2sh2Wsh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_34'; + const isP2shP2Wpkh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_22'; + const isP2shP2Wsh = isP2sh && !!vin.witness && op === 'OP_PUSHBYTES_34'; switch (true) { - // Native Segwit - P2WPKH/P2WSH (Bech32) + // Native Segwit - P2WPKH/P2WSH/P2TR case isP2wpkh: case isP2wsh: case isP2tr: // maximal gains: the scriptSig is moved entirely to the witness part - realizedBech32Gains += witnessSize(vin) * 3; + // if taproot is used savings are 42 WU higher because it produces smaller signatures and doesn't require a pubkey in the witness + // this number is explained above `realizedTaprootGains += 42;` + realizedSegwitGains += (witnessSize(vin) + (isP2tr ? 42 : 0)) * 3; // XXX P2WSH output creation is more expensive, should we take this into consideration? break; // Backward compatible Segwit - P2SH-P2WPKH - case isP2sh2Wpkh: - // the scriptSig is moved to the witness, but we have extra 21 extra non-witness bytes (48 WU) - realizedBech32Gains += witnessSize(vin) * 3 - P2SH_P2WPKH_COST; - potentialBech32Gains += P2SH_P2WPKH_COST; + case isP2shP2Wpkh: + // the scriptSig is moved to the witness, but we have extra 21 extra non-witness bytes (84 WU) + realizedSegwitGains += witnessSize(vin) * 3 - P2SH_P2WPKH_COST; + potentialSegwitGains += P2SH_P2WPKH_COST; break; // Backward compatible Segwit - P2SH-P2WSH - case isP2sh2Wsh: - // the scriptSig is moved to the witness, but we have extra 35 extra non-witness bytes - realizedBech32Gains += witnessSize(vin) * 3 - P2SH_P2WSH_COST; - potentialBech32Gains += P2SH_P2WSH_COST; + case isP2shP2Wsh: + // the scriptSig is moved to the witness, but we have extra 35 extra non-witness bytes (140 WU) + realizedSegwitGains += witnessSize(vin) * 3 - P2SH_P2WSH_COST; + potentialSegwitGains += P2SH_P2WSH_COST; break; // Non-segwit P2PKH/P2SH/P2PK/bare multisig @@ -56,9 +58,13 @@ export function calcSegwitFeeGains(tx: Transaction) { case isP2sh: case isP2pk: case isBareMultisig: { - const fullGains = scriptSigSize(vin) * 3; - potentialBech32Gains += fullGains; - potentialP2shGains += fullGains - (isP2pkh ? P2SH_P2WPKH_COST : P2SH_P2WSH_COST); + let fullGains = scriptSigSize(vin) * 3; + if (isBareMultisig) { + // a _bare_ multisig has the keys in the output script, but P2SH and P2WSH require them to be in the scriptSig/scriptWitness + fullGains -= vin.prevout.scriptpubkey.length / 2; + } + potentialSegwitGains += fullGains; + potentialP2shSegwitGains += fullGains - (isP2pkh ? P2SH_P2WPKH_COST : P2SH_P2WSH_COST); break; } } @@ -79,11 +85,11 @@ export function calcSegwitFeeGains(tx: Transaction) { // TODO maybe add some complex scripts that are specified somewhere, so that size is known, such as lightning scripts } } else { - const script = isP2sh2Wsh || isP2wsh ? vin.inner_witnessscript_asm : vin.inner_redeemscript_asm; + const script = isP2shP2Wsh || isP2wsh ? vin.inner_witnessscript_asm : vin.inner_redeemscript_asm; let replacementSize: number; if ( // single sig - isP2pk || isP2pkh || isP2wpkh || isP2sh2Wpkh || + isP2pk || isP2pkh || isP2wpkh || isP2shP2Wpkh || // multisig isBareMultisig || parseMultisigScript(script) ) { @@ -105,11 +111,11 @@ export function calcSegwitFeeGains(tx: Transaction) { // returned as percentage of the total tx weight return { - realizedBech32Gains: realizedBech32Gains / (tx.weight + realizedBech32Gains), // percent of the pre-segwit tx size - potentialBech32Gains: potentialBech32Gains / tx.weight, - potentialP2shGains: potentialP2shGains / tx.weight, + realizedSegwitGains: realizedSegwitGains / (tx.weight + realizedSegwitGains), // percent of the pre-segwit tx size + potentialSegwitGains: potentialSegwitGains / tx.weight, + potentialP2shSegwitGains: potentialP2shSegwitGains / tx.weight, potentialTaprootGains: potentialTaprootGains / tx.weight, - realizedTaprootGains: realizedTaprootGains / tx.weight + realizedTaprootGains: realizedTaprootGains / (tx.weight + realizedTaprootGains) }; } @@ -188,12 +194,12 @@ export function moveDec(num: number, n: number) { return neg + (int || '0') + (frac.length ? '.' + frac : ''); } -function zeros(n) { +function zeros(n: number) { return new Array(n + 1).join('0'); } // Formats a number for display. Treats the number as a string to avoid rounding errors. -export const formatNumber = (s, precision = null) => { +export const formatNumber = (s: number | string, precision: number | null = null) => { let [ whole, dec ] = s.toString().split('.'); // divide numbers into groups of three separated with a thin space (U+202F, "NARROW NO-BREAK SPACE"), @@ -219,27 +225,27 @@ const witnessSize = (vin: Vin) => vin.witness ? vin.witness.reduce((S, w) => S + const scriptSigSize = (vin: Vin) => vin.scriptsig ? vin.scriptsig.length / 2 : 0; // Power of ten wrapper -export function selectPowerOfTen(val: number) { +export function selectPowerOfTen(val: number): { divider: number, unit: string } { const powerOfTen = { exa: Math.pow(10, 18), peta: Math.pow(10, 15), - terra: Math.pow(10, 12), + tera: Math.pow(10, 12), giga: Math.pow(10, 9), mega: Math.pow(10, 6), kilo: Math.pow(10, 3), }; - let selectedPowerOfTen; + let selectedPowerOfTen: { divider: number, unit: string }; if (val < powerOfTen.kilo) { selectedPowerOfTen = { divider: 1, unit: '' }; // no scaling } else if (val < powerOfTen.mega) { selectedPowerOfTen = { divider: powerOfTen.kilo, unit: 'k' }; } else if (val < powerOfTen.giga) { selectedPowerOfTen = { divider: powerOfTen.mega, unit: 'M' }; - } else if (val < powerOfTen.terra) { + } else if (val < powerOfTen.tera) { selectedPowerOfTen = { divider: powerOfTen.giga, unit: 'G' }; } else if (val < powerOfTen.peta) { - selectedPowerOfTen = { divider: powerOfTen.terra, unit: 'T' }; + selectedPowerOfTen = { divider: powerOfTen.tera, unit: 'T' }; } else if (val < powerOfTen.exa) { selectedPowerOfTen = { divider: powerOfTen.peta, unit: 'P' }; } else { diff --git a/frontend/src/app/components/tx-features/tx-features.component.html b/frontend/src/app/components/tx-features/tx-features.component.html index 16dbb66f4..e3569de8d 100644 --- a/frontend/src/app/components/tx-features/tx-features.component.html +++ b/frontend/src/app/components/tx-features/tx-features.component.html @@ -1,12 +1,12 @@ -SegWit +SegWit - SegWit - - SegWit + SegWit + + SegWit -Taproot +Taproot Taproot diff --git a/frontend/src/app/components/tx-features/tx-features.component.ts b/frontend/src/app/components/tx-features/tx-features.component.ts index f73d8ae8a..4c0611971 100644 --- a/frontend/src/app/components/tx-features/tx-features.component.ts +++ b/frontend/src/app/components/tx-features/tx-features.component.ts @@ -12,9 +12,9 @@ export class TxFeaturesComponent implements OnChanges { @Input() tx: Transaction; segwitGains = { - realizedBech32Gains: 0, - potentialBech32Gains: 0, - potentialP2shGains: 0, + realizedSegwitGains: 0, + potentialSegwitGains: 0, + potentialP2shSegwitGains: 0, potentialTaprootGains: 0, realizedTaprootGains: 0 };