Liquid: Display unconfidential address and fix tracking

fixes #761
This commit is contained in:
softsimon 2021-09-06 10:20:31 +04:00
parent 844b640c8c
commit 2d007b9100
No known key found for this signature in database
GPG Key ID: 488D7DCFB5A430D7
10 changed files with 60 additions and 12 deletions

View File

@ -98,12 +98,15 @@ export namespace IBitcoinApi {
export interface AddressInformation {
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
isvalid_parent?: boolean; // (boolean) Elements only
address: string; // (string) The bitcoin address validated
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
isscript: boolean; // (boolean) If the key is a script
iswitness: boolean; // (boolean) If the address is a witness
witness_version?: boolean; // (numeric, optional) The version number of the witness program
witness_program: string; // (string, optional) The hex value of the witness program
confidential_key?: string; // (string) Elements only
unconfidential?: string; // (string) Elements only
}
export interface ChainTips {

View File

@ -60,13 +60,12 @@ class BitcoinApi implements AbstractBitcoinApi {
return this.bitcoindClient.getBlock(hash, 0);
}
$getBlockHash(height: number): Promise<string> {
return this.bitcoindClient.getBlockHash(height);
}
$getBlockHeader(hash: string): Promise<string> {
return this.bitcoindClient.getBlockHeader(hash,false);
return this.bitcoindClient.getBlockHeader(hash, false);
}
async $getBlock(hash: string): Promise<IEsploraApi.Block> {
@ -238,10 +237,6 @@ class BitcoinApi implements AbstractBitcoinApi {
});
}
protected $validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
return this.bitcoindClient.validateAddress(address);
}
private $getMempoolEntry(txid: string): Promise<IBitcoinApi.MempoolEntry> {
return this.bitcoindClient.getMempoolEntry(txid);
}

View File

@ -44,6 +44,10 @@ class BitcoinBaseApi {
$getBlockchainInfo(): Promise<IBitcoinApi.BlockchainInfo> {
return this.bitcoindClient.getBlockchainInfo();
}
$validateAddress(address: string): Promise<IBitcoinApi.AddressInformation> {
return this.bitcoindClient.validateAddress(address);
}
}
export default new BitcoinBaseApi();

View File

@ -11,6 +11,7 @@ import * as sha256 from 'crypto-js/sha256';
import * as hexEnc from 'crypto-js/enc-hex';
import loadingIndicators from '../loading-indicators';
import memoryCache from '../memory-cache';
import bitcoinBaseApi from './bitcoin-base.api';
class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
private electrumClient: any;
@ -44,7 +45,7 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
}
async $getAddress(address: string): Promise<IEsploraApi.Address> {
const addressInfo = await this.$validateAddress(address);
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
if (!addressInfo || !addressInfo.isvalid) {
return ({
'address': address,
@ -98,7 +99,7 @@ class BitcoindElectrsApi extends BitcoinApi implements AbstractBitcoinApi {
}
async $getAddressTransactions(address: string, lastSeenTxId: string): Promise<IEsploraApi.Transaction[]> {
const addressInfo = await this.$validateAddress(address);
const addressInfo = await bitcoinBaseApi.$validateAddress(address);
if (!addressInfo || !addressInfo.isvalid) {
return [];
}

View File

@ -251,6 +251,7 @@ class Server {
.get(config.MEMPOOL.API_URL_PREFIX + 'address/:address/txs', routes.getAddressTransactions)
.get(config.MEMPOOL.API_URL_PREFIX + 'address/:address/txs/chain/:txId', routes.getAddressTransactions)
.get(config.MEMPOOL.API_URL_PREFIX + 'address-prefix/:prefix', routes.getAddressPrefix)
.get(config.MEMPOOL.API_URL_PREFIX + 'validate-address/:address', routes.validateAddress)
;
}
}

View File

@ -17,6 +17,7 @@ import transactionUtils from './api/transaction-utils';
import blocks from './api/blocks';
import loadingIndicators from './api/loading-indicators';
import { Common } from './api/common';
import bitcoinBaseApi from './api/bitcoin/bitcoin-base.api';
class Routes {
constructor() {}
@ -687,6 +688,15 @@ class Routes {
}
}
public async validateAddress(req: Request, res: Response) {
try {
const result = await bitcoinBaseApi.$validateAddress(req.params.address);
res.json(result);
} catch (e) {
res.status(500).send(e instanceof Error ? e.message : e);
}
}
public getTransactionOutspends(req: Request, res: Response) {
res.status(501).send('Not implemented');
}
@ -727,7 +737,7 @@ class Routes {
const timeAvg = timeAvgMins * 60;
const remainingTime = remainingBlocks * timeAvg;
const estimatedRetargetDate = remainingTime + now;
const result = {
progressPercent,
difficultyChange,
@ -737,7 +747,7 @@ class Routes {
previousRetarget,
nextRetargetHeight,
timeAvg,
}
};
res.json(result);
} catch (e) {

View File

@ -18,6 +18,10 @@
<div class="col-md">
<table class="table table-borderless table-striped">
<tbody>
<tr *ngIf="addressInfo && addressInfo.unconfidential">
<td i18n="address.unconfidential">Unconfidential</td>
<td><a [routerLink]="['/address/' | relativeUrl, addressInfo.unconfidential]">{{ addressInfo.unconfidential }}</a> <app-clipboard [text]="addressInfo.unconfidential"></app-clipboard></td>
</tr>
<ng-template [ngIf]="!address.electrum">
<tr>
<td i18n="address.total-received">Total received</td>

View File

@ -9,6 +9,7 @@ import { AudioService } from 'src/app/services/audio.service';
import { ApiService } from 'src/app/services/api.service';
import { of, merge, Subscription, Observable } from 'rxjs';
import { SeoService } from 'src/app/services/seo.service';
import { AddressInformation } from 'src/app/interfaces/node-api.interface';
@Component({
selector: 'app-address',
@ -27,6 +28,7 @@ export class AddressComponent implements OnInit, OnDestroy {
error: any;
mainSubscription: Subscription;
addressLoadingStatus$: Observable<number>;
addressInfo: null | AddressInformation = null;
totalConfirmedTxCount = 0;
loadedConfirmedTxCount = 0;
@ -67,6 +69,7 @@ export class AddressComponent implements OnInit, OnDestroy {
this.address = null;
this.isLoadingTransactions = true;
this.transactions = null;
this.addressInfo = null;
document.body.scrollTo(0, 0);
this.addressString = params.get('id') || '';
this.seoService.setTitle($localize`:@@address.component.browser-title:Address: ${this.addressString}:INTERPOLATION:`);
@ -92,10 +95,20 @@ export class AddressComponent implements OnInit, OnDestroy {
)
.pipe(
filter((address) => !!address),
tap((address: Address) => {
if (this.stateService.network === 'liquid' && /^([m-zA-HJ-NP-Z1-9]{26,35}|[a-z]{2,5}1[ac-hj-np-z02-9]{8,100}|[a-km-zA-HJ-NP-Z1-9]{80})$/.test(address.address)) {
this.apiService.validateAddress$(address.address)
.subscribe((addressInfo) => {
this.addressInfo = addressInfo;
this.websocketService.startTrackAddress(addressInfo.unconfidential);
});
} else {
this.websocketService.startTrackAddress(address.address);
}
}),
switchMap((address) => {
this.address = address;
this.updateChainStats();
this.websocketService.startTrackAddress(address.address);
this.isLoadingAddress = false;
this.isLoadingTransactions = true;
return this.electrsApiService.getAddressTransactions$(address.address);

View File

@ -34,3 +34,16 @@ export interface DifficultyAdjustment {
remainingBlocks: number;
remainingTime: number;
}
export interface AddressInformation {
isvalid: boolean; // (boolean) If the address is valid or not. If not, this is the only property returned.
isvalid_parent?: boolean; // (boolean) Elements only
address: string; // (string) The bitcoin address validated
scriptPubKey: string; // (string) The hex-encoded scriptPubKey generated by the address
isscript: boolean; // (boolean) If the key is a script
iswitness: boolean; // (boolean) If the address is a witness
witness_version?: boolean; // (numeric, optional) The version number of the witness program
witness_program: string; // (string, optional) The hex value of the witness program
confidential_key?: string; // (string) Elements only
unconfidential?: string; // (string) Elements only
}

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment } from '../interfaces/node-api.interface';
import { CpfpInfo, OptimizedMempoolStats, DifficultyAdjustment, AddressInformation } from '../interfaces/node-api.interface';
import { Observable } from 'rxjs';
import { StateService } from './state.service';
import { WebsocketResponse } from '../interfaces/websocket.interface';
@ -96,4 +96,8 @@ export class ApiService {
getDifficultyAdjustment$(): Observable<DifficultyAdjustment> {
return this.httpClient.get<DifficultyAdjustment>(this.apiBaseUrl + this.apiBasePath + '/api/v1/difficulty-adjustment');
}
validateAddress$(address: string): Observable<AddressInformation> {
return this.httpClient.get<AddressInformation>(this.apiBaseUrl + this.apiBasePath + '/api/v1/validate-address/' + address);
}
}