parent
							
								
									2ba506515c
								
							
						
					
					
						commit
						fb63817282
					
				@ -2,7 +2,7 @@ import { IEsploraApi } from './esplora-api.interface';
 | 
			
		||||
 | 
			
		||||
export interface AbstractBitcoinApi {
 | 
			
		||||
  $getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]>;
 | 
			
		||||
  $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean): Promise<IEsploraApi.Transaction>;
 | 
			
		||||
  $getRawTransaction(txId: string, skipConversion?: boolean, addPrevout?: boolean, lazyPrevouts?: boolean): Promise<IEsploraApi.Transaction>;
 | 
			
		||||
  $getBlockHeightTip(): Promise<number>;
 | 
			
		||||
  $getTxIdsForBlock(hash: string): Promise<string[]>;
 | 
			
		||||
  $getBlockHash(height: number): Promise<string>;
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,8 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getRawTransaction(txId: string, skipConversion = false, addPrevout = false): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
 | 
			
		||||
  $getRawTransaction(txId: string, skipConversion = false, addPrevout = false, lazyPrevouts = false): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
    // If the transaction is in the mempool we already converted and fetched the fee. Only prevouts are missing
 | 
			
		||||
    const txInMempool = mempool.getMempool()[txId];
 | 
			
		||||
    if (txInMempool && addPrevout) {
 | 
			
		||||
@ -46,7 +47,7 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
          });
 | 
			
		||||
          return transaction;
 | 
			
		||||
        }
 | 
			
		||||
        return this.$convertTransaction(transaction, addPrevout);
 | 
			
		||||
        return this.$convertTransaction(transaction, addPrevout, lazyPrevouts);
 | 
			
		||||
      })
 | 
			
		||||
      .catch((e: Error) => {
 | 
			
		||||
        if (e.message.startsWith('The genesis block coinbase')) {
 | 
			
		||||
@ -126,7 +127,7 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    const outSpends: IEsploraApi.Outspend[] = [];
 | 
			
		||||
    const tx = await this.$getRawTransaction(txId, true, false);
 | 
			
		||||
    for (let i = 0; i < tx.vout.length; i++) {
 | 
			
		||||
      if (tx.status && tx.status.block_height == 0) {
 | 
			
		||||
      if (tx.status && tx.status.block_height === 0) {
 | 
			
		||||
        outSpends.push({
 | 
			
		||||
          spent: false
 | 
			
		||||
        });
 | 
			
		||||
@ -145,7 +146,7 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    return this.bitcoindClient.getNetworkHashPs(120, blockHeight);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
  protected async $convertTransaction(transaction: IBitcoinApi.Transaction, addPrevout: boolean, lazyPrevouts = false): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
    let esploraTransaction: IEsploraApi.Transaction = {
 | 
			
		||||
      txid: transaction.txid,
 | 
			
		||||
      version: transaction.version,
 | 
			
		||||
@ -192,7 +193,7 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (addPrevout) {
 | 
			
		||||
      esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction);
 | 
			
		||||
      esploraTransaction = await this.$calculateFeeFromInputs(esploraTransaction, false, lazyPrevouts);
 | 
			
		||||
    } else if (!transaction.confirmations) {
 | 
			
		||||
      esploraTransaction = await this.$appendMempoolFeeData(esploraTransaction);
 | 
			
		||||
    }
 | 
			
		||||
@ -268,20 +269,32 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    return this.bitcoindClient.getRawMemPool(true);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
 | 
			
		||||
  private async $calculateFeeFromInputs(transaction: IEsploraApi.Transaction, addPrevout: boolean, lazyPrevouts: boolean): Promise<IEsploraApi.Transaction> {
 | 
			
		||||
    if (transaction.vin[0].is_coinbase) {
 | 
			
		||||
      transaction.fee = 0;
 | 
			
		||||
      return transaction;
 | 
			
		||||
    }
 | 
			
		||||
    let totalIn = 0;
 | 
			
		||||
    for (const vin of transaction.vin) {
 | 
			
		||||
      const innerTx = await this.$getRawTransaction(vin.txid, false, false);
 | 
			
		||||
      vin.prevout = innerTx.vout[vin.vout];
 | 
			
		||||
      this.addInnerScriptsToVin(vin);
 | 
			
		||||
      totalIn += innerTx.vout[vin.vout].value;
 | 
			
		||||
 | 
			
		||||
    for (let i = 0; i < transaction.vin.length; i++) {
 | 
			
		||||
      if (lazyPrevouts && i > 12) {
 | 
			
		||||
        transaction.vin[i].lazy = true;
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
      const innerTx = await this.$getRawTransaction(transaction.vin[i].txid, false, false);
 | 
			
		||||
      if (addPrevout) {
 | 
			
		||||
        transaction.vin[i].prevout = innerTx.vout[transaction.vin[i].vout];
 | 
			
		||||
        this.addInnerScriptsToVin(transaction.vin[i]);
 | 
			
		||||
      }
 | 
			
		||||
      totalIn += innerTx.vout[transaction.vin[i].vout].value;
 | 
			
		||||
    }
 | 
			
		||||
    if (lazyPrevouts && transaction.vin.length > 12) {
 | 
			
		||||
      transaction.fee = -1;
 | 
			
		||||
    } else {
 | 
			
		||||
      const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0);
 | 
			
		||||
      transaction.fee = parseFloat((totalIn - totalOut).toFixed(8));
 | 
			
		||||
    }
 | 
			
		||||
    const totalOut = transaction.vout.reduce((p, output) => p + output.value, 0);
 | 
			
		||||
    transaction.fee = parseFloat((totalIn - totalOut).toFixed(8));
 | 
			
		||||
    return transaction;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -33,6 +33,8 @@ export namespace IEsploraApi {
 | 
			
		||||
    // Elements
 | 
			
		||||
    is_pegin?: boolean;
 | 
			
		||||
    issuance?: Issuance;
 | 
			
		||||
    // Custom
 | 
			
		||||
    lazy?: boolean;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  interface Issuance {
 | 
			
		||||
 | 
			
		||||
@ -21,8 +21,8 @@ class TransactionUtils {
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public async $getTransactionExtended(txId: string, addPrevouts = false): Promise<TransactionExtended> {
 | 
			
		||||
    const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts);
 | 
			
		||||
  public async $getTransactionExtended(txId: string, addPrevouts = false, lazyPrevouts = false): Promise<TransactionExtended> {
 | 
			
		||||
    const transaction: IEsploraApi.Transaction = await bitcoinApi.$getRawTransaction(txId, false, addPrevouts, lazyPrevouts);
 | 
			
		||||
    return this.extendTransaction(transaction);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -778,7 +778,7 @@ class Routes {
 | 
			
		||||
      const endIndex = Math.min(startingIndex + 10, txIds.length);
 | 
			
		||||
      for (let i = startingIndex; i < endIndex; i++) {
 | 
			
		||||
        try {
 | 
			
		||||
          const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true);
 | 
			
		||||
          const transaction = await transactionUtils.$getTransactionExtended(txIds[i], true, true);
 | 
			
		||||
          transactions.push(transaction);
 | 
			
		||||
          loadingIndicators.setProgress('blocktxs-' + req.params.hash, (i + 1) / endIndex * 100);
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
 | 
			
		||||
@ -125,7 +125,7 @@ export class TransactionComponent implements OnInit, OnDestroy {
 | 
			
		||||
        }),
 | 
			
		||||
        switchMap(() => {
 | 
			
		||||
          let transactionObservable$: Observable<Transaction>;
 | 
			
		||||
          if (history.state.data) {
 | 
			
		||||
          if (history.state.data && history.state.data.fee !== -1) {
 | 
			
		||||
            transactionObservable$ = of(history.state.data);
 | 
			
		||||
          } else {
 | 
			
		||||
            transactionObservable$ = this.electrsApiService
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,10 @@
 | 
			
		||||
                    </ng-container>
 | 
			
		||||
                    <ng-container *ngSwitchDefault>
 | 
			
		||||
                      <ng-template [ngIf]="!vin.prevout" [ngIfElse]="defaultAddress">
 | 
			
		||||
                        <span>{{ vin.issuance ? 'Issuance' : 'UNKNOWN' }}</span>
 | 
			
		||||
                        <span *ngIf="vin.lazy; else defaultNoPrevout" class="skeleton-loader"></span>
 | 
			
		||||
                        <ng-template #defaultNoPrevout>
 | 
			
		||||
                          <span>{{ vin.issuance ? 'Issuance' : 'UNKNOWN' }}</span>
 | 
			
		||||
                        </ng-template>
 | 
			
		||||
                      </ng-template>
 | 
			
		||||
                      <ng-template #defaultAddress>
 | 
			
		||||
                        <a [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey_address]" title="{{ vin.prevout.scriptpubkey_address }}">
 | 
			
		||||
@ -87,6 +90,7 @@
 | 
			
		||||
                    </ng-template>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <ng-template #defaultOutput>
 | 
			
		||||
                    <span *ngIf="vin.lazy" class="skeleton-loader"></span>
 | 
			
		||||
                    <app-amount *ngIf="vin.prevout" [satoshis]="vin.prevout.value"></app-amount>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                </td>
 | 
			
		||||
@ -141,7 +145,7 @@
 | 
			
		||||
            </ng-template>
 | 
			
		||||
            <tr *ngIf="tx.vin.length > 12 && tx['@vinLimit']">
 | 
			
		||||
              <td colspan="3" class="text-center">
 | 
			
		||||
                <button class="btn btn-sm btn-primary mt-2" (click)="tx['@vinLimit'] = false;"><span i18n="show-all">Show all</span> ({{ tx.vin.length - 10 }})</button>
 | 
			
		||||
                <button class="btn btn-sm btn-primary mt-2" (click)="loadMoreInputs(tx);"><span i18n="show-all">Show all</span> ({{ tx.vin.length - 10 }})</button>
 | 
			
		||||
              </td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </tbody>
 | 
			
		||||
@ -261,7 +265,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <div class="summary">
 | 
			
		||||
      <div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase">
 | 
			
		||||
      <div class="float-left mt-2-5" *ngIf="!transactionPage && !tx.vin[0].is_coinbase && tx.fee !== -1">
 | 
			
		||||
        {{ tx.fee / (tx.weight / 4) | feeRounding }} <span class="symbol" i18n="shared.sat-vbyte|sat/vB">sat/vB</span> <span class="d-none d-sm-inline-block"> – {{ tx.fee | number }} <span class="symbol" i18n="shared.sat|sat">sat</span> <span class="fiat"><app-fiat [value]="tx.fee"></app-fiat></span></span>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -175,6 +175,17 @@ export class TransactionsListComponent implements OnInit, OnChanges {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadMoreInputs(tx: Transaction) {
 | 
			
		||||
    tx['@vinLimit'] = false;
 | 
			
		||||
 | 
			
		||||
    this.electrsApiService.getTransaction$(tx.txid)
 | 
			
		||||
      .subscribe((newTx) => {
 | 
			
		||||
        tx.vin = newTx.vin;
 | 
			
		||||
        tx.fee = newTx.fee;
 | 
			
		||||
        this.ref.markForCheck();
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy() {
 | 
			
		||||
    this.outspendsSubscription.unsubscribe();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -54,6 +54,8 @@ export interface Vin {
 | 
			
		||||
  // Elements
 | 
			
		||||
  is_pegin?: boolean;
 | 
			
		||||
  issuance?: Issuance;
 | 
			
		||||
  // Custom
 | 
			
		||||
  lazy?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface Issuance {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user