Support P2PK address types
This commit is contained in:
		
							parent
							
								
									3f36a30d1d
								
							
						
					
					
						commit
						65dbafd2ec
					
				@ -14,6 +14,8 @@ export interface AbstractBitcoinApi {
 | 
			
		||||
  $getAddress(address: string): Promise<IEsploraApi.Address>;
 | 
			
		||||
  $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]>;
 | 
			
		||||
  $getAddressPrefix(prefix: string): string[];
 | 
			
		||||
  $getScriptHash(scripthash: string): Promise<IEsploraApi.ScriptHash>;
 | 
			
		||||
  $getScriptHashTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]>;
 | 
			
		||||
  $sendRawTransaction(rawTransaction: string): Promise<string>;
 | 
			
		||||
  $getOutspend(txId: string, vout: number): Promise<IEsploraApi.Outspend>;
 | 
			
		||||
  $getOutspends(txId: string): Promise<IEsploraApi.Outspend[]>;
 | 
			
		||||
 | 
			
		||||
@ -108,6 +108,14 @@ class BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    throw new Error('Method getAddressTransactions not supported by the Bitcoin RPC API.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getScriptHash(scripthash: string): Promise<IEsploraApi.ScriptHash> {
 | 
			
		||||
    throw new Error('Method getScriptHash not supported by the Bitcoin RPC API.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getScriptHashTransactions(scripthash: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
 | 
			
		||||
    throw new Error('Method getScriptHashTransactions not supported by the Bitcoin RPC API.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getRawMempool(): Promise<IEsploraApi.Transaction['txid'][]> {
 | 
			
		||||
    return this.bitcoindClient.getRawMemPool();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -121,6 +121,8 @@ class BitcoinRoutes {
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'block-height/:height', this.getBlockHeight)
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'address/:address', this.getAddress)
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'address/:address/txs', this.getAddressTransactions)
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'scripthash/:scripthash', this.getScriptHash)
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'scripthash/:scripthash/txs', this.getScriptHashTransactions)
 | 
			
		||||
          .get(config.MEMPOOL.API_URL_PREFIX + 'address-prefix/:prefix', this.getAddressPrefix)
 | 
			
		||||
          ;
 | 
			
		||||
      }
 | 
			
		||||
@ -567,6 +569,45 @@ class BitcoinRoutes {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async getScriptHash(req: Request, res: Response) {
 | 
			
		||||
    if (config.MEMPOOL.BACKEND === 'none') {
 | 
			
		||||
      res.status(405).send('Address lookups cannot be used with bitcoind as backend.');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      const addressData = await bitcoinApi.$getScriptHash(req.params.address);
 | 
			
		||||
      res.json(addressData);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
 | 
			
		||||
        return res.status(413).send(e instanceof Error ? e.message : e);
 | 
			
		||||
      }
 | 
			
		||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async getScriptHashTransactions(req: Request, res: Response): Promise<void> {
 | 
			
		||||
    if (config.MEMPOOL.BACKEND === 'none') {
 | 
			
		||||
      res.status(405).send('Address lookups cannot be used with bitcoind as backend.');
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      let lastTxId: string = '';
 | 
			
		||||
      if (req.query.after_txid && typeof req.query.after_txid === 'string') {
 | 
			
		||||
        lastTxId = req.query.after_txid;
 | 
			
		||||
      }
 | 
			
		||||
      const transactions = await bitcoinApi.$getScriptHashTransactions(req.params.address, lastTxId);
 | 
			
		||||
      res.json(transactions);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e instanceof Error && e.message && (e.message.indexOf('too long') > 0 || e.message.indexOf('confirmed status') > 0)) {
 | 
			
		||||
        res.status(413).send(e instanceof Error ? e.message : e);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      res.status(500).send(e instanceof Error ? e.message : e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async getAddressPrefix(req: Request, res: Response) {
 | 
			
		||||
    try {
 | 
			
		||||
      const blockHash = await bitcoinApi.$getAddressPrefix(req.params.prefix);
 | 
			
		||||
 | 
			
		||||
@ -126,6 +126,77 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async $getScriptHash(scripthash: string): Promise<IEsploraApi.ScriptHash> {
 | 
			
		||||
    try {
 | 
			
		||||
      const balance = await this.electrumClient.blockchainScripthash_getBalance(scripthash);
 | 
			
		||||
      let history = memoryCache.get<IElectrumApi.ScriptHashHistory[]>('Scripthash_getHistory', scripthash);
 | 
			
		||||
      if (!history) {
 | 
			
		||||
        history = await this.electrumClient.blockchainScripthash_getHistory(scripthash);
 | 
			
		||||
        memoryCache.set('Scripthash_getHistory', scripthash, history, 2);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const unconfirmed = history ? history.filter((h) => h.fee).length : 0;
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        'scripthash': scripthash,
 | 
			
		||||
        'chain_stats': {
 | 
			
		||||
          'funded_txo_count': 0,
 | 
			
		||||
          'funded_txo_sum': balance.confirmed ? balance.confirmed : 0,
 | 
			
		||||
          'spent_txo_count': 0,
 | 
			
		||||
          'spent_txo_sum': balance.confirmed < 0 ? balance.confirmed : 0,
 | 
			
		||||
          'tx_count': (history?.length || 0) - unconfirmed,
 | 
			
		||||
        },
 | 
			
		||||
        'mempool_stats': {
 | 
			
		||||
          'funded_txo_count': 0,
 | 
			
		||||
          'funded_txo_sum': balance.unconfirmed > 0 ? balance.unconfirmed : 0,
 | 
			
		||||
          'spent_txo_count': 0,
 | 
			
		||||
          'spent_txo_sum': balance.unconfirmed < 0 ? -balance.unconfirmed : 0,
 | 
			
		||||
          'tx_count': unconfirmed,
 | 
			
		||||
        },
 | 
			
		||||
        'electrum': true,
 | 
			
		||||
      };
 | 
			
		||||
    } catch (e: any) {
 | 
			
		||||
      throw new Error(typeof e === 'string' ? e : e && e.message || e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async $getScriptHashTransactions(scripthash: string, lastSeenTxId?: string): Promise<IEsploraApi.Transaction[]> {
 | 
			
		||||
    try {
 | 
			
		||||
      loadingIndicators.setProgress('address-' + scripthash, 0);
 | 
			
		||||
 | 
			
		||||
      const transactions: IEsploraApi.Transaction[] = [];
 | 
			
		||||
      let history = memoryCache.get<IElectrumApi.ScriptHashHistory[]>('Scripthash_getHistory', scripthash);
 | 
			
		||||
      if (!history) {
 | 
			
		||||
        history = await this.electrumClient.blockchainScripthash_getHistory(scripthash);
 | 
			
		||||
        memoryCache.set('Scripthash_getHistory', scripthash, history, 2);
 | 
			
		||||
      }
 | 
			
		||||
      if (!history) {
 | 
			
		||||
        throw new Error('failed to get scripthash history');
 | 
			
		||||
      }
 | 
			
		||||
      history.sort((a, b) => (b.height || 9999999) - (a.height || 9999999));
 | 
			
		||||
 | 
			
		||||
      let startingIndex = 0;
 | 
			
		||||
      if (lastSeenTxId) {
 | 
			
		||||
        const pos = history.findIndex((historicalTx) => historicalTx.tx_hash === lastSeenTxId);
 | 
			
		||||
        if (pos) {
 | 
			
		||||
          startingIndex = pos + 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      const endIndex = Math.min(startingIndex + 10, history.length);
 | 
			
		||||
 | 
			
		||||
      for (let i = startingIndex; i < endIndex; i++) {
 | 
			
		||||
        const tx = await this.$getRawTransaction(history[i].tx_hash, false, true);
 | 
			
		||||
        transactions.push(tx);
 | 
			
		||||
        loadingIndicators.setProgress('address-' + scripthash, (i + 1) / endIndex * 100);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return transactions;
 | 
			
		||||
    } catch (e: any) {
 | 
			
		||||
      loadingIndicators.setProgress('address-' + scripthash, 100);
 | 
			
		||||
      throw new Error(typeof e === 'string' ? e : e && e.message || e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private $getScriptHashBalance(scriptHash: string): Promise<IElectrumApi.ScriptHashBalance> {
 | 
			
		||||
    return this.electrumClient.blockchainScripthash_getBalance(this.encodeScriptHash(scriptHash));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -99,6 +99,13 @@ export namespace IEsploraApi {
 | 
			
		||||
    electrum?: boolean;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export interface ScriptHash {
 | 
			
		||||
    scripthash: string;
 | 
			
		||||
    chain_stats: ChainStats;
 | 
			
		||||
    mempool_stats: MempoolStats;
 | 
			
		||||
    electrum?: boolean;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  export interface ChainStats {
 | 
			
		||||
    funded_txo_count: number;
 | 
			
		||||
    funded_txo_sum: number;
 | 
			
		||||
 | 
			
		||||
@ -110,6 +110,14 @@ class ElectrsApi implements AbstractBitcoinApi {
 | 
			
		||||
    throw new Error('Method getAddressTransactions not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getScriptHash(scripthash: string): Promise<IEsploraApi.ScriptHash> {
 | 
			
		||||
    throw new Error('Method getAddress not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getScriptHashTransactions(scripthash: string, txId?: string): Promise<IEsploraApi.Transaction[]> {
 | 
			
		||||
    throw new Error('Method getAddressTransactions not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  $getAddressPrefix(prefix: string): string[] {
 | 
			
		||||
    throw new Error('Method not implemented.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -281,3 +281,12 @@ export function isFeatureActive(network: string, height: number, feature: 'rbf'
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export async function calcScriptHash$(script: string): Promise<string> {
 | 
			
		||||
  const buf = Uint8Array.from(script.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
 | 
			
		||||
  const hashBuffer = await crypto.subtle.digest('SHA-256', buf);
 | 
			
		||||
  const hashArray = Array.from(new Uint8Array(hashBuffer));
 | 
			
		||||
  return hashArray
 | 
			
		||||
    .map((bytes) => bytes.toString(16).padStart(2, '0'))
 | 
			
		||||
    .join('');
 | 
			
		||||
}
 | 
			
		||||
@ -64,13 +64,15 @@ export class AddressPreviewComponent implements OnInit, OnDestroy {
 | 
			
		||||
          this.address = null;
 | 
			
		||||
          this.addressInfo = null;
 | 
			
		||||
          this.addressString = params.get('id') || '';
 | 
			
		||||
          if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(this.addressString)) {
 | 
			
		||||
          if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[A-F0-9]{130}$/.test(this.addressString)) {
 | 
			
		||||
            this.addressString = this.addressString.toLowerCase();
 | 
			
		||||
          }
 | 
			
		||||
          this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
 | 
			
		||||
 | 
			
		||||
          return this.electrsApiService.getAddress$(this.addressString)
 | 
			
		||||
            .pipe(
 | 
			
		||||
          return (this.addressString.match(/[a-f0-9]{130}/)
 | 
			
		||||
              ? this.electrsApiService.getPubKeyAddress$(this.addressString)
 | 
			
		||||
              : this.electrsApiService.getAddress$(this.addressString)
 | 
			
		||||
            ).pipe(
 | 
			
		||||
              catchError((err) => {
 | 
			
		||||
                this.isLoadingAddress = false;
 | 
			
		||||
                this.error = err;
 | 
			
		||||
 | 
			
		||||
@ -81,6 +81,7 @@ h1 {
 | 
			
		||||
    top: 11px;
 | 
			
		||||
	}
 | 
			
		||||
  @media (min-width: 768px) {
 | 
			
		||||
    max-width: calc(100% - 180px);
 | 
			
		||||
    top: 17px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
 | 
			
		||||
import { ActivatedRoute, ParamMap } from '@angular/router';
 | 
			
		||||
import { ElectrsApiService } from '../../services/electrs-api.service';
 | 
			
		||||
import { switchMap, filter, catchError, map, tap } from 'rxjs/operators';
 | 
			
		||||
import { Address, Transaction } from '../../interfaces/electrs.interface';
 | 
			
		||||
import { Address, ScriptHash, Transaction } from '../../interfaces/electrs.interface';
 | 
			
		||||
import { WebsocketService } from '../../services/websocket.service';
 | 
			
		||||
import { StateService } from '../../services/state.service';
 | 
			
		||||
import { AudioService } from '../../services/audio.service';
 | 
			
		||||
@ -72,7 +72,7 @@ export class AddressComponent implements OnInit, OnDestroy {
 | 
			
		||||
          this.addressInfo = null;
 | 
			
		||||
          document.body.scrollTo(0, 0);
 | 
			
		||||
          this.addressString = params.get('id') || '';
 | 
			
		||||
          if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}$/.test(this.addressString)) {
 | 
			
		||||
          if (/^[A-Z]{2,5}1[AC-HJ-NP-Z02-9]{8,100}|[A-F0-9]{130}$/.test(this.addressString)) {
 | 
			
		||||
            this.addressString = this.addressString.toLowerCase();
 | 
			
		||||
          }
 | 
			
		||||
          this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
 | 
			
		||||
@ -83,8 +83,11 @@ export class AddressComponent implements OnInit, OnDestroy {
 | 
			
		||||
              .pipe(filter((state) => state === 2 && this.transactions && this.transactions.length > 0))
 | 
			
		||||
          )
 | 
			
		||||
          .pipe(
 | 
			
		||||
            switchMap(() => this.electrsApiService.getAddress$(this.addressString)
 | 
			
		||||
              .pipe(
 | 
			
		||||
            switchMap(() => (
 | 
			
		||||
              this.addressString.match(/[a-f0-9]{130}/)
 | 
			
		||||
              ? this.electrsApiService.getPubKeyAddress$(this.addressString)
 | 
			
		||||
              : this.electrsApiService.getAddress$(this.addressString)
 | 
			
		||||
            ).pipe(
 | 
			
		||||
                catchError((err) => {
 | 
			
		||||
                  this.isLoadingAddress = false;
 | 
			
		||||
                  this.error = err;
 | 
			
		||||
@ -114,7 +117,9 @@ export class AddressComponent implements OnInit, OnDestroy {
 | 
			
		||||
          this.updateChainStats();
 | 
			
		||||
          this.isLoadingAddress = false;
 | 
			
		||||
          this.isLoadingTransactions = true;
 | 
			
		||||
          return this.electrsApiService.getAddressTransactions$(address.address);
 | 
			
		||||
          return address.is_pubkey
 | 
			
		||||
              ? this.electrsApiService.getScriptHashTransactions$('41' + address.address + 'ac')
 | 
			
		||||
              : this.electrsApiService.getAddressTransactions$(address.address);
 | 
			
		||||
        }),
 | 
			
		||||
        switchMap((transactions) => {
 | 
			
		||||
          this.tempTransactions = transactions;
 | 
			
		||||
 | 
			
		||||
@ -56,7 +56,9 @@
 | 
			
		||||
                      <span i18n="transactions-list.peg-in">Peg-in</span>
 | 
			
		||||
                    </ng-container>
 | 
			
		||||
                    <ng-container *ngSwitchCase="vin.prevout && vin.prevout.scriptpubkey_type === 'p2pk'">
 | 
			
		||||
                      <span>P2PK</span>
 | 
			
		||||
                      <span>P2PK <a class="address p2pk-address" [routerLink]="['/address/' | relativeUrl, vin.prevout.scriptpubkey.slice(2, 132)]" title="{{ vin.prevout.scriptpubkey.slice(2, 132) }}">
 | 
			
		||||
                        <app-truncate [text]="vin.prevout.scriptpubkey.slice(2, 132)" [lastChars]="8"></app-truncate>
 | 
			
		||||
                      </a></span>
 | 
			
		||||
                    </ng-container>
 | 
			
		||||
                    <ng-container *ngSwitchDefault>
 | 
			
		||||
                      <ng-template [ngIf]="!vin.prevout" [ngIfElse]="defaultAddress">
 | 
			
		||||
@ -182,12 +184,19 @@
 | 
			
		||||
            <ng-template ngFor let-vout let-vindex="index" [ngForOf]="tx.vout.slice(0, getVoutLimit(tx))" [ngForTrackBy]="trackByIndexFn">
 | 
			
		||||
              <tr [ngClass]="{
 | 
			
		||||
                'assetBox': assetsMinimal && assetsMinimal[vout.asset] && vout.scriptpubkey_address && tx.vin && !tx.vin[0].is_coinbase && tx._unblinded || outputIndex === vindex,
 | 
			
		||||
                'highlight': vout.scriptpubkey_address === this.address && this.address !== ''
 | 
			
		||||
                'highlight': this.address !== '' && (vout.scriptpubkey_address === this.address || (vout.scriptpubkey_type === 'p2pk' && vout.scriptpubkey.slice(2, 132) === this.address))
 | 
			
		||||
              }">
 | 
			
		||||
                <td class="address-cell">
 | 
			
		||||
                  <a class="address" *ngIf="vout.scriptpubkey_address; else scriptpubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
 | 
			
		||||
                  <a class="address" *ngIf="vout.scriptpubkey_address; else pubkey_type" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey_address]" title="{{ vout.scriptpubkey_address }}">
 | 
			
		||||
                    <app-truncate [text]="vout.scriptpubkey_address" [lastChars]="8"></app-truncate>
 | 
			
		||||
                  </a>
 | 
			
		||||
                  <ng-template #pubkey_type>
 | 
			
		||||
                    <ng-container *ngIf="vout.scriptpubkey_type === 'p2pk'; else scriptpubkey_type">
 | 
			
		||||
                      P2PK <a class="address p2pk-address" [routerLink]="['/address/' | relativeUrl, vout.scriptpubkey.slice(2, 132)]" title="{{ vout.scriptpubkey.slice(2, 132) }}">
 | 
			
		||||
                        <app-truncate [text]="vout.scriptpubkey.slice(2, 132)" [lastChars]="8"></app-truncate>
 | 
			
		||||
                      </a>
 | 
			
		||||
                    </ng-container>
 | 
			
		||||
                  </ng-template>
 | 
			
		||||
                  <div>
 | 
			
		||||
                    <app-address-labels [vout]="vout" [channel]="tx._channels && tx._channels.outputs[vindex] ? tx._channels.outputs[vindex] : null"></app-address-labels>
 | 
			
		||||
                  </div>
 | 
			
		||||
 | 
			
		||||
@ -140,6 +140,12 @@ h2 {
 | 
			
		||||
	font-family: monospace;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.p2pk-address {
 | 
			
		||||
	display: inline-block;
 | 
			
		||||
	margin-left: 1em;
 | 
			
		||||
	max-width: 140px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.grey-info-text {
 | 
			
		||||
	color:#6c757d;
 | 
			
		||||
	font-style: italic;
 | 
			
		||||
 | 
			
		||||
@ -129,6 +129,22 @@ export interface Address {
 | 
			
		||||
  address: string;
 | 
			
		||||
  chain_stats: ChainStats;
 | 
			
		||||
  mempool_stats: MempoolStats;
 | 
			
		||||
  is_pubkey?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ScriptHash {
 | 
			
		||||
  electrum?: boolean;
 | 
			
		||||
  scripthash: string;
 | 
			
		||||
  chain_stats: ChainStats;
 | 
			
		||||
  mempool_stats: MempoolStats;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface AddressOrScriptHash {
 | 
			
		||||
  electrum?: boolean;
 | 
			
		||||
  address?: string;
 | 
			
		||||
  scripthash?: string;
 | 
			
		||||
  chain_stats: ChainStats;
 | 
			
		||||
  mempool_stats: MempoolStats;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ChainStats {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient, HttpParams } from '@angular/common/http';
 | 
			
		||||
import { Observable } from 'rxjs';
 | 
			
		||||
import { Transaction, Address, Outspend, Recent, Asset } from '../interfaces/electrs.interface';
 | 
			
		||||
import { Observable, from, of, switchMap } from 'rxjs';
 | 
			
		||||
import { Transaction, Address, Outspend, Recent, Asset, ScriptHash } from '../interfaces/electrs.interface';
 | 
			
		||||
import { StateService } from './state.service';
 | 
			
		||||
import { BlockExtended } from '../interfaces/node-api.interface';
 | 
			
		||||
import { calcScriptHash$ } from '../bitcoin.utils';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -65,6 +66,24 @@ export class ElectrsApiService {
 | 
			
		||||
    return this.httpClient.get<Address>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPubKeyAddress$(pubkey: string): Observable<Address> {
 | 
			
		||||
    return this.getScriptHash$('41' + pubkey + 'ac').pipe(
 | 
			
		||||
      switchMap((scripthash: ScriptHash) => {
 | 
			
		||||
        return of({
 | 
			
		||||
          ...scripthash,
 | 
			
		||||
          address: pubkey,
 | 
			
		||||
          is_pubkey: true,
 | 
			
		||||
        });
 | 
			
		||||
      })
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getScriptHash$(script: string): Observable<ScriptHash> {
 | 
			
		||||
    return from(calcScriptHash$(script)).pipe(
 | 
			
		||||
      switchMap(scriptHash => this.httpClient.get<ScriptHash>(this.apiBaseUrl + this.apiBasePath + '/api/scripthash/' + scriptHash))
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAddressTransactions$(address: string,  txid?: string): Observable<Transaction[]> {
 | 
			
		||||
    let params = new HttpParams();
 | 
			
		||||
    if (txid) {
 | 
			
		||||
@ -73,6 +92,16 @@ export class ElectrsApiService {
 | 
			
		||||
    return this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/address/' + address + '/txs', { params });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getScriptHashTransactions$(script: string,  txid?: string): Observable<Transaction[]> {
 | 
			
		||||
    let params = new HttpParams();
 | 
			
		||||
    if (txid) {
 | 
			
		||||
      params = params.append('after_txid', txid);
 | 
			
		||||
    }
 | 
			
		||||
    return from(calcScriptHash$(script)).pipe(
 | 
			
		||||
      switchMap(scriptHash => this.httpClient.get<Transaction[]>(this.apiBaseUrl + this.apiBasePath + '/api/scripthash/' + scriptHash + '/txs', { params })),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getAsset$(assetId: string): Observable<Asset> {
 | 
			
		||||
    return this.httpClient.get<Asset>(this.apiBaseUrl + this.apiBasePath + '/api/asset/' + assetId);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user