diff --git a/frontend/src/app/components/address-labels/address-labels.component.html b/frontend/src/app/components/address-labels/address-labels.component.html index ec59684a5..353e733ae 100644 --- a/frontend/src/app/components/address-labels/address-labels.component.html +++ b/frontend/src/app/components/address-labels/address-labels.component.html @@ -1,4 +1,13 @@ -{{ label }} + + {{ label }} + + + + {{ label }} + \ No newline at end of file diff --git a/frontend/src/app/components/address-labels/address-labels.component.ts b/frontend/src/app/components/address-labels/address-labels.component.ts index c5892fd8a..dbac4ab11 100644 --- a/frontend/src/app/components/address-labels/address-labels.component.ts +++ b/frontend/src/app/components/address-labels/address-labels.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core'; +import { Component, ChangeDetectionStrategy, Input, OnChanges } from '@angular/core'; import { Vin, Vout } from '../../interfaces/electrs.interface'; import { StateService } from 'src/app/services/state.service'; @@ -8,11 +8,12 @@ import { StateService } from 'src/app/services/state.service'; styleUrls: ['./address-labels.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AddressLabelsComponent implements OnInit { +export class AddressLabelsComponent implements OnChanges { network = ''; @Input() vin: Vin; @Input() vout: Vout; + @Input() channel: any; label?: string; @@ -22,14 +23,20 @@ export class AddressLabelsComponent implements OnInit { this.network = stateService.network; } - ngOnInit() { - if (this.vin) { + ngOnChanges() { + if (this.channel) { + this.handleChannel(); + } else if (this.vin) { this.handleVin(); } else if (this.vout) { this.handleVout(); } } + handleChannel() { + this.label = `Channel open: ${this.channel.alias_left} <> ${this.channel.alias_right}`; + } + handleVin() { if (this.vin.inner_witnessscript_asm) { if (this.vin.inner_witnessscript_asm.indexOf('OP_DEPTH OP_PUSHNUM_12 OP_EQUAL OP_IF OP_PUSHNUM_11') === 0) { diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.html b/frontend/src/app/components/transactions-list/transactions-list.component.html index 16e6b9b2f..9119355f6 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.html +++ b/frontend/src/app/components/transactions-list/transactions-list.component.html @@ -172,7 +172,7 @@
- +
diff --git a/frontend/src/app/components/transactions-list/transactions-list.component.ts b/frontend/src/app/components/transactions-list/transactions-list.component.ts index d5ec36151..ab0a742cf 100644 --- a/frontend/src/app/components/transactions-list/transactions-list.component.ts +++ b/frontend/src/app/components/transactions-list/transactions-list.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit, Input, ChangeDetectionStrategy, OnChanges, Output, EventEmitter, ChangeDetectorRef } from '@angular/core'; import { StateService } from '../../services/state.service'; -import { Observable, forkJoin, ReplaySubject, BehaviorSubject, merge, Subscription } from 'rxjs'; +import { Observable, ReplaySubject, BehaviorSubject, merge, Subscription } from 'rxjs'; import { Outspend, Transaction, Vin, Vout } from '../../interfaces/electrs.interface'; import { ElectrsApiService } from '../../services/electrs-api.service'; import { environment } from 'src/environments/environment'; @@ -32,9 +32,11 @@ export class TransactionsListComponent implements OnInit, OnChanges { latestBlock$: Observable; outspendsSubscription: Subscription; refreshOutspends$: ReplaySubject = new ReplaySubject(); + refreshChannels$: ReplaySubject = new ReplaySubject(); showDetails$ = new BehaviorSubject(false); outspends: Outspend[][] = []; assetsMinimal: any; + channels: any[]; constructor( public stateService: StateService, @@ -73,7 +75,15 @@ export class TransactionsListComponent implements OnInit, OnChanges { }; } }), - ) + ), + this.refreshChannels$ + .pipe( + switchMap((txIds) => this.apiService.getChannelByTxIds$(txIds)), + map((channels) => { + this.channels = channels; + }), + ) + , ).subscribe(() => this.ref.markForCheck()); } @@ -114,8 +124,9 @@ export class TransactionsListComponent implements OnInit, OnChanges { tx['addressValue'] = addressIn - addressOut; } }); - - this.refreshOutspends$.next(this.transactions.map((tx) => tx.txid)); + const txIds = this.transactions.map((tx) => tx.txid); + this.refreshOutspends$.next(txIds); + this.refreshChannels$.next(txIds); } onScroll() { diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts index a0b3d8ff7..ad552ffb1 100644 --- a/frontend/src/app/services/api.service.ts +++ b/frontend/src/app/services/api.service.ts @@ -231,4 +231,13 @@ export class ApiService { getRewardStats$(blockCount: number = 144): Observable { return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + `/api/v1/mining/reward-stats/${blockCount}`); } + + getChannelByTxIds$(txIds: string[]): Observable { + let params = new HttpParams(); + txIds.forEach((txId: string) => { + params = params.append('txId[]', txId); + }); + return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/lightning/api/v1/channels/txids/', { params }); + } + } diff --git a/lightning-backend/src/api/explorer/channels.api.ts b/lightning-backend/src/api/explorer/channels.api.ts index c13bc9319..be2c953f5 100644 --- a/lightning-backend/src/api/explorer/channels.api.ts +++ b/lightning-backend/src/api/explorer/channels.api.ts @@ -8,7 +8,7 @@ class ChannelsApi { const [rows]: any = await DB.query(query); return rows; } catch (e) { - logger.err('$getChannel error: ' + (e instanceof Error ? e.message : e)); + logger.err('$getAllChannels error: ' + (e instanceof Error ? e.message : e)); throw e; } } @@ -19,7 +19,7 @@ class ChannelsApi { const [rows]: any = await DB.query(query, [status]); return rows; } catch (e) { - logger.err('$getChannel error: ' + (e instanceof Error ? e.message : e)); + logger.err('$getChannelsByStatus error: ' + (e instanceof Error ? e.message : e)); throw e; } } @@ -46,6 +46,17 @@ class ChannelsApi { } } + public async $getChannelByTransactionId(transactionId: string): Promise { + try { + const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE channels.transaction_id = ?`; + const [rows]: any = await DB.query(query, [transactionId]); + return rows[0]; + } catch (e) { + logger.err('$getChannelByTransactionId error: ' + (e instanceof Error ? e.message : e)); + throw e; + } + } + public async $getChannelsForNode(public_key: string): Promise { try { const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE node1_public_key = ? OR node2_public_key = ?`; diff --git a/lightning-backend/src/api/explorer/channels.routes.ts b/lightning-backend/src/api/explorer/channels.routes.ts index 4cb3f8b1d..07dabdd31 100644 --- a/lightning-backend/src/api/explorer/channels.routes.ts +++ b/lightning-backend/src/api/explorer/channels.routes.ts @@ -7,6 +7,7 @@ class ChannelsRoutes { public initRoutes(app: Express) { app + .get(config.MEMPOOL.API_URL_PREFIX + 'channels/txids', this.$getChannelsByTransactionIds) .get(config.MEMPOOL.API_URL_PREFIX + 'channels/:short_id', this.$getChannel) .get(config.MEMPOOL.API_URL_PREFIX + 'channels', this.$getChannels) ; @@ -38,6 +39,29 @@ class ChannelsRoutes { } } + private async $getChannelsByTransactionIds(req: Request, res: Response) { + try { + if (!Array.isArray(req.query.txId)) { + res.status(500).send('Not an array'); + return; + } + const txIds: string[] = []; + for (const _txId in req.query.txId) { + if (typeof req.query.txId[_txId] === 'string') { + txIds.push(req.query.txId[_txId].toString()); + } + } + const channels: any[] = []; + for (const txId of txIds) { + const channel = await channelsApi.$getChannelByTransactionId(txId); + channels.push(channel); + } + res.json(channels); + } catch (e) { + res.status(500).send(e instanceof Error ? e.message : e); + } + } + } export default new ChannelsRoutes();