Display Segwit and RBF information
This commit is contained in:
		
							parent
							
								
									84e15b133a
								
							
						
					
					
						commit
						3deedada07
					
				
							
								
								
									
										68
									
								
								frontend/src/app/bitcoin.utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								frontend/src/app/bitcoin.utils.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					import { Transaction, Vin } from './interfaces/electrs.interface';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const P2SH_P2WPKH_COST = 21 * 4; // the WU cost for the non-witness part of P2SH-P2WPKH
 | 
				
			||||||
 | 
					const P2SH_P2WSH_COST  = 35 * 4; // the WU cost for the non-witness part of P2SH-P2WSH
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function calcSegwitFeeGains(tx: Transaction) {
 | 
				
			||||||
 | 
					  // calculated in weight units
 | 
				
			||||||
 | 
					  let realizedGains = 0;
 | 
				
			||||||
 | 
					  let potentialBech32Gains = 0;
 | 
				
			||||||
 | 
					  let potentialP2shGains = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const vin of tx.vin) {
 | 
				
			||||||
 | 
					    if (!vin.prevout) { continue; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const isP2pkh = vin.prevout.scriptpubkey_type === 'p2pkh';
 | 
				
			||||||
 | 
					    const isP2sh  = vin.prevout.scriptpubkey_type === 'p2sh';
 | 
				
			||||||
 | 
					    const isP2wsh = vin.prevout.scriptpubkey_type === 'v0_p2wsh';
 | 
				
			||||||
 | 
					    const isP2wpkh = vin.prevout.scriptpubkey_type === 'v0_p2wpkh';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (true) {
 | 
				
			||||||
 | 
					      // Native Segwit - P2WPKH/P2WSH (Bech32)
 | 
				
			||||||
 | 
					      case isP2wpkh:
 | 
				
			||||||
 | 
					      case isP2wsh:
 | 
				
			||||||
 | 
					        // maximal gains: the scriptSig is moved entirely to the witness part
 | 
				
			||||||
 | 
					        realizedGains += witnessSize(vin) * 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)
 | 
				
			||||||
 | 
					        realizedGains += witnessSize(vin) * 3 - P2SH_P2WPKH_COST;
 | 
				
			||||||
 | 
					        potentialBech32Gains += 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
 | 
				
			||||||
 | 
					        realizedGains += witnessSize(vin) * 3 - P2SH_P2WSH_COST;
 | 
				
			||||||
 | 
					        potentialBech32Gains += P2SH_P2WSH_COST;
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Non-segwit P2PKH/P2SH
 | 
				
			||||||
 | 
					      case isP2pkh:
 | 
				
			||||||
 | 
					      case isP2sh:
 | 
				
			||||||
 | 
					        const fullGains = scriptSigSize(vin) * 3;
 | 
				
			||||||
 | 
					        potentialBech32Gains += fullGains;
 | 
				
			||||||
 | 
					        potentialP2shGains += fullGains - (isP2pkh ? P2SH_P2WPKH_COST : P2SH_P2WSH_COST);
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // TODO: should we also consider P2PK and pay-to-bare-script (non-p2sh-wrapped) as upgradable to P2WPKH and P2WSH?
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // returned as percentage of the total tx weight
 | 
				
			||||||
 | 
					  return { realizedGains: realizedGains / (tx.weight + realizedGains) // percent of the pre-segwit tx size
 | 
				
			||||||
 | 
					         , potentialBech32Gains: potentialBech32Gains / tx.weight
 | 
				
			||||||
 | 
					         , potentialP2shGains: potentialP2shGains / tx.weight
 | 
				
			||||||
 | 
					         };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Utilities for segwitFeeGains
 | 
				
			||||||
 | 
					const witnessSize = (vin: Vin) => vin.witness.reduce((S, w) => S + (w.length / 2), 0);
 | 
				
			||||||
 | 
					const scriptSigSize = (vin: Vin) => vin.scriptsig ? vin.scriptsig.length / 2 : 0;
 | 
				
			||||||
@ -43,6 +43,7 @@
 | 
				
			|||||||
                    <td>After <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
 | 
					                    <td>After <app-timespan [time]="tx.status.block_time - transactionTime"></app-timespan></td>
 | 
				
			||||||
                  </tr>
 | 
					                  </tr>
 | 
				
			||||||
                </ng-template>
 | 
					                </ng-template>
 | 
				
			||||||
 | 
					                <ng-container *ngTemplateOutlet="features"></ng-container>
 | 
				
			||||||
              </tbody>
 | 
					              </tbody>
 | 
				
			||||||
            </table>
 | 
					            </table>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
@ -115,6 +116,7 @@
 | 
				
			|||||||
                    </ng-template>
 | 
					                    </ng-template>
 | 
				
			||||||
                  </td>
 | 
					                  </td>
 | 
				
			||||||
                </tr>
 | 
					                </tr>
 | 
				
			||||||
 | 
					                <ng-container *ngTemplateOutlet="features"></ng-container>
 | 
				
			||||||
              </tbody>
 | 
					              </tbody>
 | 
				
			||||||
            </table>
 | 
					            </table>
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
@ -250,3 +252,15 @@
 | 
				
			|||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<br>
 | 
					<br>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<ng-template #features>
 | 
				
			||||||
 | 
					  <tr *ngIf="network !== 'liquid'">
 | 
				
			||||||
 | 
					    <td class="td-width">Features</td>
 | 
				
			||||||
 | 
					    <td>
 | 
				
			||||||
 | 
					      <span *ngIf="segwitGains.realizedGains && !segwitGains.potentialBech32Gains" class="badge badge-success mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number:  '1.0-0' }}% on fees by using native SegWit-Bech32" placement="bottom">SegWit</span>
 | 
				
			||||||
 | 
					      <span *ngIf="segwitGains.realizedGains && segwitGains.potentialBech32Gains" class="badge badge-warning mr-1" ngbTooltip="This transaction saved {{ segwitGains.realizedGains * 100 | number:  '1.0-0' }}% on fees by using SegWit and could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% more by fully upgrading to native SegWit-Bech32" placement="bottom">SegWit</span>
 | 
				
			||||||
 | 
					      <span *ngIf="segwitGains.potentialP2shGains" class="badge badge-danger mr-1" ngbTooltip="This transaction could save {{ segwitGains.potentialBech32Gains * 100 | number : '1.0-0' }}% on fees by upgrading to native SegWit-Bech32 or {{ segwitGains.potentialP2shGains * 100 | number:  '1.0-0' }}% by upgrading to SegWit-P2SH" placement="bottom"><del>SegWit</del></span>
 | 
				
			||||||
 | 
					      <span *ngIf="isRbfTransaction" class="badge badge-success" ngbTooltip="This transaction support Replace-By-Fee (RBF) allowing fee bumping" placement="bottom">RBF</span>
 | 
				
			||||||
 | 
					    </td>
 | 
				
			||||||
 | 
					  </tr>
 | 
				
			||||||
 | 
					</ng-template>
 | 
				
			||||||
@ -9,6 +9,7 @@ import { WebsocketService } from '../../services/websocket.service';
 | 
				
			|||||||
import { AudioService } from 'src/app/services/audio.service';
 | 
					import { AudioService } from 'src/app/services/audio.service';
 | 
				
			||||||
import { ApiService } from 'src/app/services/api.service';
 | 
					import { ApiService } from 'src/app/services/api.service';
 | 
				
			||||||
import { SeoService } from 'src/app/services/seo.service';
 | 
					import { SeoService } from 'src/app/services/seo.service';
 | 
				
			||||||
 | 
					import { calcSegwitFeeGains } from 'src/app/bitcoin.utils';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'app-transaction',
 | 
					  selector: 'app-transaction',
 | 
				
			||||||
@ -29,6 +30,12 @@ export class TransactionComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
  latestBlock: Block;
 | 
					  latestBlock: Block;
 | 
				
			||||||
  transactionTime = -1;
 | 
					  transactionTime = -1;
 | 
				
			||||||
  subscription: Subscription;
 | 
					  subscription: Subscription;
 | 
				
			||||||
 | 
					  segwitGains = {
 | 
				
			||||||
 | 
					    realizedGains: 0,
 | 
				
			||||||
 | 
					    potentialBech32Gains: 0,
 | 
				
			||||||
 | 
					    potentialP2shGains: 0,
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  isRbfTransaction: boolean;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    private route: ActivatedRoute,
 | 
					    private route: ActivatedRoute,
 | 
				
			||||||
@ -79,6 +86,8 @@ export class TransactionComponent implements OnInit, OnDestroy {
 | 
				
			|||||||
      this.error = undefined;
 | 
					      this.error = undefined;
 | 
				
			||||||
      this.waitingForTransaction = false;
 | 
					      this.waitingForTransaction = false;
 | 
				
			||||||
      this.setMempoolBlocksSubscription();
 | 
					      this.setMempoolBlocksSubscription();
 | 
				
			||||||
 | 
					      this.segwitGains = calcSegwitFeeGains(tx);
 | 
				
			||||||
 | 
					      this.isRbfTransaction = tx.vin.some((v) => v.sequence < 0xfffffffe);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      if (!tx.status.confirmed) {
 | 
					      if (!tx.status.confirmed) {
 | 
				
			||||||
        this.websocketService.startTrackTransaction(tx.txid);
 | 
					        this.websocketService.startTrackTransaction(tx.txid);
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@ $nav-tabs-link-active-bg: #11131f;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
$primary: #105fb0;
 | 
					$primary: #105fb0;
 | 
				
			||||||
$secondary: #2d3348;
 | 
					$secondary: #2d3348;
 | 
				
			||||||
 | 
					$success: #1a9436;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$link-color:                #1bd8f4;
 | 
					$link-color:                #1bd8f4;
 | 
				
			||||||
$link-decoration:           none !default;
 | 
					$link-decoration:           none !default;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user