Release 0.9.3 (#532)

ECL - Channel Force Close Bug Fix #523
ECL - Routing Filter Bug Fix #525
ECL - Adding Routing Peers Tab #527
LND - Open Channel Bump Fee #529
pull/563/head v0.9.3
ShahanaFarooqui 4 years ago committed by GitHub
parent 58c46ebf71
commit 80516e1ffe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -270,56 +270,6 @@ MIT
angularx-qrcode
MIT
base64-js
MIT
The MIT License (MIT)
Copyright (c) 2014 Jameson Little
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
buffer
MIT
The MIT License (MIT)
Copyright (c) Feross Aboukhadijeh, and other contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
convert-hex
convert-string
@ -419,21 +369,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
ieee754
BSD-3-Clause
Copyright 2008 Fair Oaks Labs, Inc.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
isarray
MIT
MIT License

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -12,8 +12,8 @@
<link rel="mask-icon" href="assets/images/favicon-light/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="styles.a87bd00d80a3f00717e3.css"></head>
<link rel="stylesheet" href="styles.bbe7b5fc38151212c389.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.557e2f220d3836209742.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.b019d1ec04d0afd2b62d.js" defer></script></body>
<script src="runtime.907d5114b7f55f73c01d.js" defer></script><script src="polyfills-es5.2ac0d98b22574ae745b1.js" nomodule defer></script><script src="polyfills.5ae721a6ae5ab597a53d.js" defer></script><script src="main.6b2c66a7af14112dcd7f.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"07193c762dd7469c63c8",6:"d5f36502e36ce33775a8",7:"e955a16c6af1870f3a25",8:"d2aae145b0c672e1b7ff"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],f=r[2],p=0,s=[];p<i.length;p++)a=i[p],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in c)Object.prototype.hasOwnProperty.call(c,n)&&(e[n]=c[n]);for(l&&l(r);s.length;)s.shift()();return u.push.apply(u,f||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++)0!==o[t[i]]&&(n=!1);n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={0:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var r=[],t=o[e];if(0!==t)if(t)r.push(t[2]);else{var n=new Promise((function(r,n){t=o[e]=[r,n]}));r.push(t[2]=n);var u,i=document.createElement("script");i.charset="utf-8",i.timeout=120,a.nc&&i.setAttribute("nonce",a.nc),i.src=function(e){return a.p+""+({}[e]||e)+"."+{1:"60ddf8569c2860edb59c",6:"d5f36502e36ce33775a8",7:"a355238233f02a06308f",8:"29ee25c8adea70ea041a"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(f);var t=o[e];if(0!==t){if(t){var n=r&&("load"===r.type?"missing":r.type),u=r&&r.target&&r.target.src;c.message="Loading chunk "+e+" failed.\n("+n+": "+u+")",c.name="ChunkLoadError",c.type=n,c.request=u,t[1](c)}o[e]=void 0}};var f=setTimeout((function(){u({type:"timeout",target:i})}),12e4);i.onerror=i.onload=u,document.head.appendChild(i)}return Promise.all(r)},a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,(function(r){return e[r]}).bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="",a.oe=function(e){throw console.error(e),e};var i=window.webpackJsonp=window.webpackJsonp||[],c=i.push.bind(i);i.push=r,i=i.slice();for(var f=0;f<i.length;f++)r(i[f]);var l=c;t()}([]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -16,6 +16,7 @@ simplifyAllChannels = (channels) => {
toLocal: (channel.data.commitments.localCommit.spec.toLocal) ? Math.round(+channel.data.commitments.localCommit.spec.toLocal/1000) : 0,
toRemote: (channel.data.commitments.localCommit.spec.toRemote) ? Math.round(+channel.data.commitments.localCommit.spec.toRemote/1000) : 0,
shortChannelId: channel.data && channel.data.shortChannelId ? channel.data.shortChannelId : '',
isFunder: channel.data && channel.data.commitments && channel.data.commitments.localParams && channel.data.commitments.localParams.isFunder ? channel.data.commitments.localParams.isFunder : false,
buried: channel.data && channel.data.buried ? channel.data.buried : false,
feeBaseMsat: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeBaseMsat ? channel.data.channelUpdate.feeBaseMsat : 0,
feeProportionalMillionths: channel.data && channel.data.channelUpdate && channel.data.channelUpdate.feeProportionalMillionths ? channel.data.channelUpdate.feeProportionalMillionths : 0,
@ -196,7 +197,7 @@ exports.updateChannelRelayFee = (req, res, next) => {
exports.closeChannel = (req, res, next) => {
options = common.getOptions();
if (!req.query.force) {
if (req.query.force !== 'true') {
options.url = common.getSelLNServerUrl() + '/close';
} else {
options.url = common.getSelLNServerUrl() + '/forceclose';

@ -139,3 +139,36 @@ exports.getUTXOs = (req, res, next) => {
});
});
}
exports.bumpFee = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/v2/wallet/bumpfee';
options.form = {};
options.form.outpoint = {
txid_str: req.body.txid,
output_index: req.body.outputIndex
};
if (req.body.targetConf) {
options.form.target_conf = req.body.targetConf;
} else if (req.body.satPerByte) {
options.form.sat_per_byte = req.body.satPerByte;
}
options.form = JSON.stringify(options.form);
request.post(options).then((body) => {
res.status(200).json(body);
})
.catch(errRes => {
let err = JSON.parse(JSON.stringify(errRes));
if (err.options && err.options.headers && err.options.headers['Grpc-Metadata-macaroon']) {
delete err.options.headers['Grpc-Metadata-macaroon'];
}
if (err.response && err.response.request && err.response.request.headers && err.response.request.headers['Grpc-Metadata-macaroon']) {
delete err.response.request.headers['Grpc-Metadata-macaroon'];
}
logger.error({fileName: 'Wallet', lineNum: 170, msg: 'Bump Fee Error: ' + JSON.stringify(err)});
return res.status(500).json({
message: "Bump fee failed!",
error: err.error
});
});
}

2
package-lock.json generated

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.9.2-beta",
"version": "0.9.3-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

@ -1,6 +1,6 @@
{
"name": "rtl",
"version": "0.9.2-beta",
"version": "0.9.3-beta",
"license": "MIT",
"scripts": {
"ng": "ng",

@ -6,6 +6,7 @@ const authCheck = require("../authCheck");
router.get("/genseed/:passphrase?", authCheck, WalletController.genSeed);
router.get("/updateSelNodeOptions", authCheck, WalletController.updateSelNodeOptions);
router.get("/getUTXOs", authCheck, WalletController.getUTXOs);
router.post("/:operation", authCheck, WalletController.operateWallet);
router.post("/wallet/:operation", authCheck, WalletController.operateWallet);
router.post("/bumpfee", authCheck, WalletController.bumpFee);
module.exports = router;

@ -27,6 +27,7 @@ import { ECLLightningPaymentsComponent } from './transactions/payments/lightning
import { ECLLightningInvoicesComponent } from './transactions/invoices/lightning-invoices.component';
import { ECLRoutingComponent } from './routing/routing.component';
import { ECLForwardingHistoryComponent } from './routing/forwarding-history/forwarding-history.component';
import { ECLRoutingPeersComponent } from './routing/routing-peers/routing-peers.component';
import { ECLLookupsComponent } from './lookups/lookups.component';
import { ECLNodeLookupComponent } from './lookups/node-lookup/node-lookup.component';
import { ECLUnlockedGuard } from '../shared/services/auth.guard';
@ -57,6 +58,7 @@ import { ECLUnlockedGuard } from '../shared/services/auth.guard';
ECLChannelInactiveTableComponent,
ECLRoutingComponent,
ECLForwardingHistoryComponent,
ECLRoutingPeersComponent,
ECLTransactionsComponent,
ECLQueryRoutesComponent,
ECLLightningPaymentsComponent,

@ -55,8 +55,8 @@
<span class="overflow-wrap foreground-secondary-text">{{channel.channelFlags === 0 ? 'Yes' : 'No'}}</span>
</div>
<div fxFlex="50">
<h4 fxLayoutAlign="start" class="font-bold-500">Buried</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.buried ? 'Yes' : 'No'}}</span>
<h4 fxLayoutAlign="start" class="font-bold-500">Funder</h4>
<span class="overflow-wrap foreground-secondary-text">{{channel.isFunder ? 'Yes' : 'No'}}</span>
</div>
</div>
<div *ngIf="showAdvanced && channelsType === 'open'">

@ -11,22 +11,26 @@
<th mat-header-cell *matHeaderCellDef mat-sort-header>Date/Time</th>
<td mat-cell *matCellDef="let fhEvent">{{fhEvent?.timestampStr}}</td>
</ng-container>
<ng-container matColumnDef="fromAlias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>In Channel</th>
<td mat-cell *matCellDef="let fhEvent" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '20rem'}">{{fhEvent?.fromChannelAlias}}</td>
</ng-container>
<ng-container matColumnDef="toAlias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Out Channel</th>
<td mat-cell *matCellDef="let fhEvent" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '20rem'}">{{fhEvent?.toChannelAlias}}</td>
</ng-container>
<ng-container matColumnDef="amountIn">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount In (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.amountIn | number}}</span></td>
</ng-container>
<ng-container matColumnDef="amountOut">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount Out (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{fhEvent?.amountOut | number}}</span></td>
</ng-container>
<ng-container matColumnDef="fee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee Earned (Sats)</th>
<td mat-cell *matCellDef="let fhEvent"><span fxLayoutAlign="end center">{{(fhEvent?.amountIn - fhEvent?.amountOut) | number}}</span></td>
</ng-container>
<ng-container matColumnDef="fromAlias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>In Channel</th>
<td mat-cell *matCellDef="let fhEvent" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '30rem'}">{{fhEvent?.fromChannelAlias}}</td>
</ng-container>
<ng-container matColumnDef="toAlias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Out Channel</th>
<td mat-cell *matCellDef="let fhEvent" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '10rem' : '30rem'}">{{fhEvent?.toChannelAlias}}</td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef class="px-3">
<div class="bordered-box table-actions-select">

@ -37,12 +37,15 @@ export class ECLForwardingHistoryComponent implements OnInit, OnChanges {
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['timestamp', 'amountIn', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.SM || this.screenSize === ScreenSizeEnum.MD) {
} else if(this.screenSize === ScreenSizeEnum.SM) {
this.flgSticky = false;
this.displayedColumns = ['timestamp', 'amountIn', 'fee', 'actions'];
} else if(this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['timestamp', 'amountIn', 'amountOut', 'fee', 'actions'];
} else {
this.flgSticky = true;
this.displayedColumns = ['timestamp', 'amountIn', 'fee', 'fromAlias', 'toAlias', 'actions'];
this.displayedColumns = ['timestamp', 'fromAlias', 'toAlias', 'amountIn', 'amountOut', 'fee', 'actions'];
}
}
@ -78,10 +81,6 @@ export class ECLForwardingHistoryComponent implements OnInit, OnChanges {
this.forwardingHistoryEvents.sort = this.sort;
this.forwardingHistoryEvents.sortingDataAccessor = (data, sortHeaderId) => (data[sortHeaderId] && isNaN(data[sortHeaderId])) ? data[sortHeaderId].toLocaleLowerCase() : +data[sortHeaderId];
this.forwardingHistoryEvents.paginator = this.paginator;
this.forwardingHistoryEvents.filterPredicate = (event: PaymentRelayed, fltr: string) => {
const newEvent = event.amountIn + event.amountOut + event.paymentHash + event.fromChannelId + event.toChannelId + event.timestampStr;
return newEvent.includes(fltr.toLowerCase());
};
this.logger.info(this.forwardingHistoryEvents);
}

@ -0,0 +1,86 @@
<div fxLayout="column" fxLayoutAlign="start stretch" class="padding-gap-large">
<div fxLayout="column" fxLayout.gt-md="row" fxFlex="100" fxLayoutAlign="space-between stretch" class="padding-gap-x page-sub-title-container">
<div fxLayout="column" fxFlex="49" fxLayoutAlign="start start">
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container w-100" [ngClass]="{'mt-2': screenSize === screenSizeEnum.XS, 'mt-1': screenSize === screenSizeEnum.SM}">
<div fxFlex="70">Incoming</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyIncomingFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start start" fxFlex="100" class="table-container w-100">
<table mat-table #tableIn [dataSource]="RoutingPeersIncoming" matSort fxFlex="100" class="overflow-auto incoming-table">
<ng-container matColumnDef="channelId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Channel ID</th>
<td mat-cell *matCellDef="let rPeer" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '5rem' : '10rem'}">{{rPeer.channelId}}</td>
</ng-container>
<ng-container matColumnDef="alias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Peer Alias</th>
<td mat-cell *matCellDef="let rPeer" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '5rem' : '10rem'}">{{rPeer.alias}}</td>
</ng-container>
<ng-container matColumnDef="events">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Events</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.events | number}}</span>
</td>
</ng-container>
<ng-container matColumnDef="totalAmount">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount (Sats)</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.totalAmount | number}}</span></td>
</ng-container>
<ng-container matColumnDef="totalFee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee (Sats)</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.totalFee | number}}</span></td>
</ng-container>
<ng-container matColumnDef="no_incoming_event">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!RoutingPeersIncoming?.data || RoutingPeersIncoming?.data?.length<1">No incoming routing peer available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_incoming_event']" [ngClass]="{'display-none': RoutingPeersIncoming?.data && RoutingPeersIncoming?.data?.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
<div fxLayout="column" fxFlex="49" fxLayoutAlign="start start">
<div fxLayout="column" fxLayout.gt-sm="row" fxLayoutAlign.gt-sm="space-between center" fxLayoutAlign="start stretch" class="padding-gap-x page-sub-title-container w-100" [ngClass]="{'mt-2': screenSize !== screenSizeEnum.LG}">
<div fxFlex="70">Outgoing</div>
<mat-form-field fxFlex="30">
<input matInput (keyup)="applyOutgoingFilter($event.target.value)" placeholder="Filter">
</mat-form-field>
</div>
<div perfectScrollbar fxLayout="row" fxLayoutAlign="start center" fxFlex="100" class="table-container w-100">
<table mat-table #tableOut [dataSource]="RoutingPeersOutgoing" matSort fxFlex="100" class="overflow-auto outgoing-table">
<ng-container matColumnDef="channelId">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Channel ID</th>
<td mat-cell *matCellDef="let rPeer" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '5rem' : '10rem'}">{{rPeer.channelId}}</td>
</ng-container>
<ng-container matColumnDef="alias">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Peer Alias</th>
<td mat-cell *matCellDef="let rPeer" [ngStyle]="{'max-width': (screenSize === screenSizeEnum.XS) ? '5rem' : '10rem'}">{{rPeer.alias}}</td>
</ng-container>
<ng-container matColumnDef="events">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Events</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.events | number}}</span>
</td>
</ng-container>
<ng-container matColumnDef="totalAmount">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Amount (Sats)</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.totalAmount | number}}</span></td>
</ng-container>
<ng-container matColumnDef="totalFee">
<th mat-header-cell *matHeaderCellDef mat-sort-header arrowPosition="before">Fee (Sats)</th>
<td mat-cell *matCellDef="let rPeer"><span fxLayoutAlign="end center">{{rPeer.totalFee | number}}</span></td>
</ng-container>
<ng-container matColumnDef="no_outgoing_event">
<td mat-footer-cell *matFooterCellDef colspan="4">
<p *ngIf="!RoutingPeersOutgoing?.data || RoutingPeersOutgoing?.data?.length<1">No outgoing routing peer available.</p>
</td>
</ng-container>
<tr mat-footer-row *matFooterRowDef="['no_outgoing_event']" [ngClass]="{'display-none': RoutingPeersOutgoing?.data && RoutingPeersOutgoing?.data?.length>0}"></tr>
<tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: flgSticky;"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</div>
</div>
</div>
</div>

@ -0,0 +1,6 @@
.mat-column-channelId, .mat-column-alias {
flex: 1 1 10%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ECLRoutingPeersComponent } from './routing-peers.component';
describe('ECLRoutingPeersComponent', () => {
let component: ECLRoutingPeersComponent;
let fixture: ComponentFixture<ECLRoutingPeersComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ECLRoutingPeersComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ECLRoutingPeersComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,101 @@
import { Component, OnInit, OnChanges, ViewChild, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { Actions } from '@ngrx/effects';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { PaymentRelayed, RoutingPeers } from '../../../shared/models/eclModels';
import { LoggerService } from '../../../shared/services/logger.service';
import { CommonService } from '../../../shared/services/common.service';
import * as fromRTLReducer from '../../../store/rtl.reducers';
import { ScreenSizeEnum } from '../../../shared/services/consts-enums-functions';
@Component({
selector: 'rtl-ecl-routing-peers',
templateUrl: './routing-peers.component.html',
styleUrls: ['./routing-peers.component.scss']
})
export class ECLRoutingPeersComponent implements OnInit, OnChanges {
@ViewChild(MatSort, { static: true }) sortIn: MatSort;
@ViewChild('tableOut', {read: MatSort, static: true}) sortOut: MatSort;
@Input() routingPeersData: any;
public displayedColumns = [];
public RoutingPeersIncoming: any;
public RoutingPeersOutgoing: any;
public flgSticky = false;
public screenSize = '';
public screenSizeEnum = ScreenSizeEnum;
constructor(private logger: LoggerService, private commonService: CommonService, private store: Store<fromRTLReducer.RTLState>, private actions$: Actions) {
this.screenSize = this.commonService.getScreenSize();
if(this.screenSize === ScreenSizeEnum.XS) {
this.flgSticky = false;
this.displayedColumns = ['alias', 'totalFee'];
} else if(this.screenSize === ScreenSizeEnum.SM) {
this.flgSticky = false;
this.displayedColumns = ['alias', 'events', 'totalFee'];
} else if(this.screenSize === ScreenSizeEnum.MD) {
this.flgSticky = false;
this.displayedColumns = ['alias', 'events', 'totalAmount', 'totalFee'];
} else {
this.flgSticky = true;
this.displayedColumns = ['channelId', 'alias', 'events', 'totalAmount', 'totalFee'];
}
}
ngOnInit() {}
ngOnChanges() {
this.loadRoutingPeersTable(this.routingPeersData);
}
loadRoutingPeersTable(forwardingEvents: PaymentRelayed[]) {
if (forwardingEvents.length > 0) {
const results = this.groupRoutingPeers(forwardingEvents);
this.RoutingPeersIncoming = new MatTableDataSource<RoutingPeers>(results[0]);
this.RoutingPeersIncoming.sort = this.sortIn;
this.logger.info(this.RoutingPeersIncoming);
this.RoutingPeersOutgoing = new MatTableDataSource<RoutingPeers>(results[1]);
this.RoutingPeersOutgoing.sort = this.sortOut;
this.logger.info(this.RoutingPeersOutgoing);
} else {
// To reset table after other Forwarding history calls
this.RoutingPeersIncoming = new MatTableDataSource<RoutingPeers>([]);
this.RoutingPeersOutgoing = new MatTableDataSource<RoutingPeers>([]);
}
}
groupRoutingPeers(forwardingEvents: PaymentRelayed[]) {
const incomingResults: RoutingPeers[] = [];
const outgoingResults: RoutingPeers[] = [];
forwardingEvents.forEach(event => {
const incoming = incomingResults.find(result => result.channelId === event.fromChannelId);
const outgoing = outgoingResults.find(result => result.channelId === event.toChannelId);
if (!incoming) {
incomingResults.push({channelId: event.fromChannelId, alias: event.fromChannelAlias, events: 1, totalAmount: +event.amountIn, totalFee: (event.amountIn - event.amountOut)});
} else {
incoming.events++;
incoming.totalAmount = +incoming.totalAmount + +event.amountIn;
incoming.totalFee = +incoming.totalFee + (event.amountIn - event.amountOut);
}
if (!outgoing) {
outgoingResults.push({channelId: event.toChannelId, alias: event.toChannelAlias, events: 1, totalAmount: +event.amountOut, totalFee: (event.amountIn - event.amountOut)});
} else {
outgoing.events++;
outgoing.totalAmount = +outgoing.totalAmount + +event.amountOut;
outgoing.totalFee = +outgoing.totalFee + (event.amountIn - event.amountOut);
}
});
return [this.commonService.sortDescByKey(incomingResults, 'totalFee'), this.commonService.sortDescByKey(outgoingResults, 'totalFee')];
}
applyIncomingFilter(selFilter: string) {
this.RoutingPeersIncoming.filter = selFilter;
}
applyOutgoingFilter(selFilter: string) {
this.RoutingPeersOutgoing.filter = selFilter;
}
}

@ -12,6 +12,10 @@
<div class="p-2" *ngIf="errorMessage !== ''">{{errorMessage}}</div>
<rtl-ecl-forwarding-history *ngIf="errorMessage === ''" [successfulEvents]="events"></rtl-ecl-forwarding-history>
</mat-tab>
<mat-tab label="Routing Peers">
<div class="p-2" *ngIf="errorMessage !== ''">{{errorMessage}}</div>
<rtl-ecl-routing-peers *ngIf="errorMessage === '' && events" [routingPeersData]="events"></rtl-ecl-routing-peers>
</mat-tab>
</mat-tab-group>
</div>
</mat-card-content>

@ -352,7 +352,7 @@ export class ECLEffects implements OnDestroy {
setTimeout(() => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new ECLActions.FetchChannels({fetchPayments: false}));
this.store.dispatch(new RTLActions.OpenSnackBar('Channel Closed Successfully!'));
this.store.dispatch(new RTLActions.OpenSnackBar(action.payload.force ? 'Channel Force Closed Successfully!' : 'Channel Closed Successfully!'));
}, 2000);
return {
type: RTLActions.VOID

@ -171,11 +171,21 @@ export function ECLReducer(state = initECLState, action: ECLActions.ECLActions)
event.toShortChannelId = storedChannels[idx].shortChannelId ? storedChannels[idx].shortChannelId : '';
if (event.fromChannelAlias) { return; }
}
if (idx === storedChannels.length-1) {
if (!event.fromChannelAlias) {
event.fromChannelAlias = event.fromChannelId.substring(0, 17) + '...';
event.fromShortChannelId = '';
}
if (!event.toChannelAlias) {
event.toChannelAlias = event.toChannelId.substring(0, 17) + '...';
event.toShortChannelId = '';
}
}
}
} else {
event.fromChannelAlias = event.fromChannelId;
event.fromChannelAlias = event.fromChannelId.substring(0, 17) + '...';
event.fromShortChannelId = '';
event.toChannelAlias = event.toChannelId;
event.toChannelAlias = event.toChannelId.substring(0, 17) + '...';
event.toShortChannelId = '';
}
});

@ -18,6 +18,7 @@ import { OnChainTransactionHistoryComponent } from './on-chain/utxo-tables/on-ch
import { WalletComponent } from './wallet/wallet.component';
import { LightningPaymentsComponent } from './transactions/payments/lightning-payments.component';
import { ChannelPendingTableComponent } from './peers-channels/channels/channels-tables/channel-pending-table/channel-pending-table.component';
import { BumpFeeComponent } from './peers-channels/channels/bump-fee-modal/bump-fee.component';
import { ChannelClosedTableComponent } from './peers-channels/channels/channels-tables/channel-closed-table/channel-closed-table.component';
import { TransactionsComponent } from './transactions/transactions.component';
import { LookupsComponent } from './lookups/lookups.component';
@ -64,6 +65,7 @@ import { LNDUnlockedGuard } from '../shared/services/auth.guard';
WalletComponent,
LightningPaymentsComponent,
ChannelPendingTableComponent,
BumpFeeComponent,
ChannelClosedTableComponent,
TransactionsComponent,
LookupsComponent,

@ -0,0 +1,62 @@
<div fxLayout="row">
<div fxFlex="100" class="padding-gap-large">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between center" class="modal-info-header">
<div fxFlex="95" fxLayoutAlign="start start">
<span class="page-title">Bump Fee</span>
</div>
<button tabindex="8" fxFlex="5" fxLayoutAlign="center" class="btn-close-x p-0" (click)="onClose()" mat-button>X</button>
</mat-card-header>
<mat-card-content class="mt-5px">
<form fxLayout="column">
<div fxLayout="column" class="bordered-box my-2 p-2">
<p fxLayoutAlign="start center" class="pb-1 word-break">Bump fee for channel point: {{bumpFeeChannel?.channel?.channel_point}}</p>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<div fxFlex="100" class="alert alert-info">
<fa-icon [icon]="faInfoCircle" class="mr-1 alert-icon"></fa-icon>
<span fxLayout="column" fxFlex="100">Bumping fee on pending open channels is an advanced feature, attempt it only if you are familiar with the functionality of Bitcoin transactions.
<div>Before attempting fee bump ensure the following:</div>
<div class="pl-1">1: Use a Bitcoin block explorer to ensure that channel opening transaction is not confirmed.</div>
<div class="pl-1">2: The channel opening transaction must have a sizable change output, which can be spent further. The fee cannot be bumped without the change output.</div>
<div class="pl-1">3: Find the index value of the change output via a block explorer.</div>
<div class="pl-1">4: Enter the index value of the change output in the form below and the desired fee rate.</div>
<div class="pl-1">5: Upon successful fee bump, use your block explorer to track the child transaction in the mempool, which should be linked with the change output transaction.</div>
</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxFlex.gt-sm="32" fxLayoutAlign="start end">
<input matInput [(ngModel)]="outputIndex" placeholder="Index for Change Output" type="number" step="1" min="0" tabindex="1" required name="outputIdx" #outputIdx="ngModel">
<mat-error *ngIf="outputIdx.errors?.required">Index for change output is required.</mat-error>
<mat-error *ngIf="outputIdx.errors?.pendingChannelOutputIndex">Invalid index value.</mat-error>
</mat-form-field>
<mat-form-field fxFlex.gt-sm="32">
<mat-select [(value)]="selTransType" (selectionChange)="blocks = null;fees = null;" tabindex="2">
<mat-option *ngFor="let transType of transTypes" [value]="transType.id">
{{transType.name}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field fxFlex.gt-sm="32" fxLayoutAlign="start end" *ngIf="selTransType=='1'">
<input matInput [(ngModel)]="blocks" placeholder="Number of Blocks" type="number" name="blocks" step="1"
min="0" required tabindex="3" #blcks="ngModel">
<mat-error *ngIf="!blocks">Number of blocks is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex.gt-sm="32" fxLayoutAlign="start end" *ngIf="selTransType=='2'">
<input matInput [(ngModel)]="fees" placeholder="Fees (Sats/Byte)"
type="number" name="fees" step="1" min="0" required tabindex="4" #fee="ngModel">
<mat-error *ngIf="!fees">Fees is required.</mat-error>
</mat-form-field>
</div>
<div fxFlex="100" class="alert alert-danger mt-1" *ngIf="bumpFeeError !== ''">
<fa-icon [icon]="faExclamationTriangle" class="mr-1 alert-icon"></fa-icon>
<span>{{bumpFeeError}}</span>
</div>
</div>
</div>
<div fxLayout="row" fxLayoutAlign="end center">
<button mat-stroked-button color="primary" type="reset" class="mr-1" (click)="resetData()" tabindex="5" default>Clear</button>
<button mat-flat-button color="primary" type="submit" tabindex="6" (click)="onBumpFee()">{{bumpFeeError !== '' ? 'Retry Bump Fee' : 'Bump Fee'}}</button>
</div>
</form>
</mat-card-content>
</div>
</div>

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BumpFeeComponent } from './bump-fee.component';
describe('BumpFeeComponent', () => {
let component: BumpFeeComponent;
let fixture: ComponentFixture<BumpFeeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ BumpFeeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BumpFeeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,78 @@
import { Component, OnInit, OnDestroy, ViewChild, Inject } from '@angular/core';
import { NgModel } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { PendingOpenChannel } from '../../../../shared/models/lndModels';
import { PendingOpenChannelInformation } from '../../../../shared/models/alertData';
import { TRANS_TYPES } from '../../../../shared/services/consts-enums-functions';
import { DataService } from '../../../../shared/services/data.service';
import * as fromRTLReducer from '../../../../store/rtl.reducers';
@Component({
selector: 'rtl-bump-fee',
templateUrl: './bump-fee.component.html',
styleUrls: ['./bump-fee.component.scss']
})
export class BumpFeeComponent implements OnInit, OnDestroy {
private outputIdx: NgModel;
@ViewChild('outputIdx') set payReq(outIdx: NgModel) {if(outIdx) { this.outputIdx = outIdx; }}
public bumpFeeChannel: PendingOpenChannel;
public transTypes = [...TRANS_TYPES];
public selTransType = '1';
public blocks = null;
public fees = null;
public outputIndex = null;
public faInfoCircle = faInfoCircle;
public faExclamationTriangle = faExclamationTriangle;
public bumpFeeError = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(public dialogRef: MatDialogRef<BumpFeeComponent>, @Inject(MAT_DIALOG_DATA) public data: PendingOpenChannelInformation, private store: Store<fromRTLReducer.RTLState>, private dataService: DataService) {}
ngOnInit() {
this.transTypes = this.transTypes.splice(1);
this.bumpFeeChannel = this.data.pendingChannel;
const channelPointArr = this.bumpFeeChannel.channel && this.bumpFeeChannel.channel.channel_point ? this.bumpFeeChannel.channel.channel_point.split(':') : [];
this.bumpFeeChannel.channel.txid_str = channelPointArr[0] ? channelPointArr[0] : (this.bumpFeeChannel.channel && this.bumpFeeChannel.channel.channel_point ? this.bumpFeeChannel.channel.channel_point : '');
this.bumpFeeChannel.channel.output_index = channelPointArr[1] ? +channelPointArr[1] : null;
}
onBumpFee() {
if (this.outputIndex === this.bumpFeeChannel.channel.output_index) {
this.outputIdx.control.setErrors({'pendingChannelOutputIndex': true});
return true;
}
if ((!this.outputIndex && this.outputIndex !== 0) || (this.selTransType === '1' && (!this.blocks || this.blocks === 0)) || (this.selTransType === '2' && (!this.fees || this.fees === 0))) { return true; }
this.dataService.bumpFee(this.bumpFeeChannel.channel.txid_str, this.outputIndex, this.blocks, this.fees).pipe(takeUntil(this.unSubs[0]))
.subscribe(res => {
this.dialogRef.close(false);
}, (err) => {
console.error(err);
this.bumpFeeError = err.message ? err.message : err;
});
}
resetData() {
this.bumpFeeError = '';
this.selTransType = '1';
this.blocks = null;
this.fees = null;
this.outputIdx.control.setErrors(null);
}
onClose() {
this.dialogRef.close(false);
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

@ -25,9 +25,15 @@
<mat-cell fxLayoutAlign="end center" *matCellDef="let channel">{{channel.channel.capacity | number}}</mat-cell>
</ng-container>
<ng-container matColumnDef="actions">
<mat-header-cell fxLayoutAlign="end center" class="px-4" *matHeaderCellDef>Actions</mat-header-cell>
<mat-cell fxLayoutAlign="end center" *matCellDef="let channel" class="px-4">
<button mat-stroked-button color="primary" type="button" tabindex="1" (click)="onOpenClick(channel)">View Info</button>
<mat-header-cell fxLayoutAlign="end center" class="pl-3 pr-4" *matHeaderCellDef>Actions</mat-header-cell>
<mat-cell fxLayoutAlign="end center" *matCellDef="let channel" class="px-3">
<div fxFlex="100" class="bordered-box table-actions-select" fxLayoutAlign="center center">
<mat-select placeholder="Actions" tabindex="1" class="mr-0">
<mat-select-trigger></mat-select-trigger>
<mat-option (click)="onOpenClick(channel)">View Info</mat-option>
<mat-option (click)="onBumpFee(channel)">Bump Fee</mat-option>
</mat-select>
</div>
</mat-cell>
</ng-container>
<ng-container matColumnDef="no_pending_open">

@ -5,10 +5,11 @@ import { Store } from '@ngrx/store';
import { MatSort } from '@angular/material/sort';
import { MatTableDataSource } from '@angular/material/table';
import { Channel, GetInfo, PendingChannels } from '../../../../../shared/models/lndModels';
import { Channel, GetInfo, PendingChannels, PendingOpenChannel } from '../../../../../shared/models/lndModels';
import { SelNodeChild } from '../../../../../shared/models/RTLconfig';
import { LoggerService } from '../../../../../shared/services/logger.service';
import { CommonService } from '../../../../../shared/services/common.service';
import { BumpFeeComponent } from '../../bump-fee-modal/bump-fee.component';
import * as RTLActions from '../../../../../store/rtl.actions';
import * as fromRTLReducer from '../../../../../store/rtl.reducers';
@ -121,6 +122,13 @@ export class ChannelPendingTableComponent implements OnInit, OnDestroy {
}}));
}
onBumpFee(selChannel: PendingOpenChannel) {
this.store.dispatch(new RTLActions.OpenAlert({ data: {
pendingChannel: selChannel,
component: BumpFeeComponent
}}));
}
onForceClosingClick(selChannel: any) {
const fcChannelObj1 = JSON.parse(JSON.stringify(selChannel, ['closing_txid', 'limbo_balance', 'maturity_height', 'blocks_til_maturity', 'recovered_balance'], 2));
const fcChannelObj2 = JSON.parse(JSON.stringify(selChannel.channel, ['remote_alias', 'channel_point', 'remote_balance', 'local_balance', 'remote_node_pub', 'capacity'], 2));

@ -919,7 +919,7 @@ export class LNDEffects implements OnDestroy {
initWallet = this.actions$.pipe(
ofType(LNDActions.INIT_WALLET_LND),
mergeMap((action: LNDActions.InitWallet) => {
return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/initwallet',
return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/wallet/initwallet',
{
wallet_password: action.payload.pwd,
cipher_seed_mnemonic: action.payload.cipher ? action.payload.cipher : '',
@ -946,7 +946,7 @@ export class LNDEffects implements OnDestroy {
unlockWallet = this.actions$.pipe(
ofType(LNDActions.UNLOCK_WALLET_LND),
mergeMap((action: LNDActions.UnlockWallet) => {
return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/unlockwallet', { wallet_password: action.payload.pwd })
return this.httpClient.post(this.CHILD_API_URL + environment.WALLET_API + '/wallet/unlockwallet', { wallet_password: action.payload.pwd })
.pipe(
map((postRes) => {
this.logger.info(postRes);

@ -265,6 +265,9 @@ export function LNDReducer(state = initLNDState, action: LNDActions.LNDActions)
if (!event.alias_out) { event.alias_out = event.chan_id_out; }
}
}
} else {
event.alias_in = event.chan_id_in;
event.alias_out = event.chan_id_out;
}
});
} else {

@ -1,6 +1,6 @@
import { DataTypeEnum, SwapTypeEnum } from '../services/consts-enums-functions';
import { GetInfoRoot, RTLConfiguration } from './RTLconfig';
import { GetInfo, Invoice, Channel, Peer } from './lndModels';
import { GetInfo, Invoice, Channel, Peer, PendingOpenChannel } from './lndModels';
import { Invoice as InvoiceCL, GetInfo as GetInfoCL, Peer as PeerCL, Channel as ChannelCL, Transaction as TransactionCL } from './clModels';
import { GetInfo as GetInfoECL, Peer as PeerECL, Channel as ChannelECL, Invoice as InvoiceECL, PaymentSent as PaymentSentECL } from './eclModels';
import { LoopQuote } from './loopModels';
@ -107,6 +107,11 @@ export interface ECLChannelInformation {
component?: any;
}
export interface PendingOpenChannelInformation {
pendingChannel: PendingOpenChannel;
component?: any;
}
export interface OnChainAddressInformation {
alertTitle?: string;
address: string;
@ -177,5 +182,5 @@ export interface DialogConfig {
width?: string;
maxWidth?: string;
minHeight?: string;
data: AlertData | ConfirmationData | ErrorData | OpenChannelAlert | CLOpenChannelAlert | InvoiceInformation | CLInvoiceInformation | ECLInvoiceInformation | ECLPaymentInformation | ChannelInformation | CLChannelInformation | OnChainAddressInformation | ShowPubkeyData | LoopAlert | AuthConfig | LoginTokenData | OnChainSendFunds | CLOnChainSendFunds | ECLChannelInformation | ECLOpenChannelAlert;
data: AlertData | ConfirmationData | ErrorData | OpenChannelAlert | CLOpenChannelAlert | InvoiceInformation | CLInvoiceInformation | ECLInvoiceInformation | ECLPaymentInformation | ChannelInformation | CLChannelInformation | PendingOpenChannelInformation | OnChainAddressInformation | ShowPubkeyData | LoopAlert | AuthConfig | LoginTokenData | OnChainSendFunds | CLOnChainSendFunds | ECLChannelInformation | ECLOpenChannelAlert;
}

@ -109,6 +109,7 @@ export interface Channel {
toLocal?: number;
toRemote?: number;
shortChannelId?: string;
isFunder?: boolean;
buried?: boolean;
feeBaseMsat?: number;
feeProportionalMillionths?: number;
@ -217,4 +218,12 @@ export interface LookupNode {
alias?: string;
addresses?: string[];
unknownFields?: string;
}
}
export interface RoutingPeers {
channelId?: string;
alias?: string;
events?: number;
totalAmount?: number;
totalFee?: number;
}

@ -119,6 +119,8 @@ export interface PendingChannel {
remote_alias?: string;
remote_node_pub?: string;
channel_point?: string;
txid_str?: string;
output_index?: number;
capacity?: string;
local_balance?: string;
remote_balance?: string;

@ -1,14 +1,14 @@
import { Injectable, OnInit, OnDestroy } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
import { Subject, throwError } from 'rxjs';
import { of, Subject, throwError } from 'rxjs';
import { map, takeUntil, catchError } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { MatSnackBar } from '@angular/material/snack-bar';
import { LoggerService } from '../../shared/services/logger.service';
import { environment, API_URL } from '../../../environments/environment';
import { ErrorMessageComponent } from '../components/data-modal/error-message/error-message.component';
import * as LNDActions from '../../lnd/store/lnd.actions';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
@ -18,7 +18,7 @@ export class DataService implements OnInit, OnDestroy {
private childAPIUrl = API_URL;
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];
constructor(private httpClient: HttpClient, private store: Store<fromRTLReducer.RTLState>, private logger: LoggerService,) {}
constructor(private httpClient: HttpClient, private store: Store<fromRTLReducer.RTLState>, private logger: LoggerService, private snackBar: MatSnackBar) {}
ngOnInit() {}
@ -130,6 +130,24 @@ export class DataService implements OnInit, OnDestroy {
}));
}
bumpFee(txid: string, outputIndex: number, targetConf: number, satPerByte: number) {
let bumpFeeBody: any = {txid: txid, outputIndex: outputIndex};
if (targetConf) { bumpFeeBody.targetConf = targetConf; }
if (satPerByte) { bumpFeeBody.satPerByte = satPerByte; }
this.store.dispatch(new RTLActions.OpenSpinner('Bumping Fee...'));
return this.httpClient.post(this.childAPIUrl + environment.WALLET_API + '/bumpfee', bumpFeeBody)
.pipe(takeUntil(this.unSubs[2]),
map((res: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
this.snackBar.open('Successfully bumped the fee. Use the block explorer to verify transaction.');
return res;
}),
catchError(err => {
this.handleErrorWithoutAlert('Bump Fee', err);
return throwError(err.error && err.error.error ? err.error.error : err.error ? err.error : err);
}));
}
handleErrorWithoutAlert(actionName: string, err: { status: number, error: any }) {
this.store.dispatch(new RTLActions.CloseSpinner());
this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err));

@ -1 +1 @@
export const VERSION = '0.9.2-beta';
export const VERSION = '0.9.3-beta';
Loading…
Cancel
Save