Sign/Verify Message #240

Sign/Verify Message #240
This commit is contained in:
Shahana Farooqui 2020-02-07 17:20:40 -05:00
parent d8835d2fcc
commit d41e6d4a46
33 changed files with 400 additions and 16 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -9,8 +9,8 @@
<link rel="icon" type="image/png" sizes="32x32" href="assets/images/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="assets/images/favicon/favicon-16x16.png">
<link rel="manifest" href="assets/images/favicon/site.webmanifest">
<link rel="stylesheet" href="styles.dc87b0a5f469f6ecc1b4.css"></head>
<link rel="stylesheet" href="styles.e6b93e564e6b46dc016e.css"></head>
<body>
<rtl-app></rtl-app>
<script src="runtime.987de13467a4c3a263de.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.d1d565730bda0bc69787.js" defer></script></body>
<script src="runtime.fd7f598087c4a68228d3.js" defer></script><script src="polyfills-es5.37b2eeccc22c1df73ce7.js" nomodule defer></script><script src="polyfills.f1c3d2a0bcdfc4e93ca8.js" defer></script><script src="main.ffc3e23c9362bb3a7234.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

View File

@ -1 +1 @@
!function(e){function r(r){for(var n,a,i=r[0],c=r[1],l=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(f&&f(r);s.length;)s.shift()();return u.push.apply(u,l||[]),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:"b78c4146e439be098e08",6:"1cabd441a095f3055cd4",7:"18eeb6e2c81e91aa476c"}[e]+".js"}(e);var c=new Error;u=function(r){i.onerror=i.onload=null,clearTimeout(l);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 l=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 l=0;l<i.length;l++)r(i[l]);var f=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:"b78c4146e439be098e08",6:"f485795c2defda71fe9b",7:"ee799a9ee1d85d393fd3"}[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

2
app.js
View File

@ -26,6 +26,7 @@ const payReqRoutes = require("./routes/lnd/payReq");
const paymentsRoutes = require("./routes/lnd/payments");
const invoiceRoutes = require("./routes/lnd/invoices");
const switchRoutes = require("./routes/lnd/switch");
const messageRoutes = require("./routes/lnd/message");
const infoCLRoutes = require("./routes/c-lightning/getInfo");
const feesCLRoutes = require("./routes/c-lightning/fees");
@ -73,6 +74,7 @@ app.use(apiLNDRoot + "payreq", payReqRoutes);
app.use(apiLNDRoot + "payments", paymentsRoutes);
app.use(apiLNDRoot + "invoices", invoiceRoutes);
app.use(apiLNDRoot + "switch", switchRoutes);
app.use(apiLNDRoot + "message", messageRoutes);
app.use(apiCLRoot + "getinfo", infoCLRoutes);
app.use(apiCLRoot + "fees", feesCLRoutes);

View File

@ -0,0 +1,57 @@
var request = require('request-promise');
var common = require('../../common');
var logger = require('../logger');
var options = {};
exports.signMessage = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/signmessage';
options.form = JSON.stringify({
msg: Buffer.from(req.body.message).toString('base64')
});
request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Signed: ' + JSON.stringify(body)});
if(!body || body.error) {
res.status(500).json({
message: "Sign message failed!",
error: (!body) ? 'Error From Server!' : body.error
});
} else {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Sign Message Failed: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Sign Message Failed!',
error: err.error
});
});
};
exports.verifyMessage = (req, res, next) => {
options = common.getOptions();
options.url = common.getSelLNServerUrl() + '/verifymessage';
options.form = JSON.stringify({
msg: Buffer.from(req.body.message).toString('base64'),
signature: req.body.signature
});
request.post(options, (error, response, body) => {
logger.info({fileName: 'Messages', msg: 'Message Verified: ' + JSON.stringify(body)});
if(!body || body.error) {
res.status(500).json({
message: "Verify message failed!",
error: (!body) ? 'Error From Server!' : body.error
});
} else {
res.status(201).json(body);
}
})
.catch(function (err) {
logger.error({fileName: 'Messages', lineNum: 24, msg: 'Message Verification Failed: ' + JSON.stringify(err)});
return res.status(500).json({
message: 'Verify Message Failed!',
error: err.error
});
});
};

9
routes/lnd/message.js Normal file
View File

@ -0,0 +1,9 @@
const MessagesController = require("../../controllers/lnd/message");
const express = require("express");
const router = express.Router();
const authCheck = require("../authCheck");
router.post("/sign", authCheck, MessagesController.signMessage);
router.post("/verify", authCheck, MessagesController.verifyMessage);
module.exports = router;

View File

@ -29,6 +29,9 @@ import { NodeLookupComponent } from './lookups/node-lookup/node-lookup.component
import { BackupComponent } from './backup/backup.component';
import { ChannelBackupTableComponent } from './backup/channel-backup-table/channel-backup-table.component';
import { ChannelRestoreTableComponent } from './backup/channel-restore-table/channel-restore-table.component';
import { SignVerifyMessageComponent } from './sign-verify-message/sign-verify-message.component';
import { SignComponent } from './sign-verify-message/sign/sign.component';
import { VerifyComponent } from './sign-verify-message/verify/verify.component';
import { QueryRoutesComponent } from './transactions/query-routes/query-routes.component';
import { ChannelOpenTableComponent } from './peers-channels/channels/channels-tables/channel-open-table/channel-open-table.component';
import { UnlockWalletComponent } from './wallet/unlock/unlock.component';
@ -69,8 +72,11 @@ import { LNDUnlockedGuard } from '../shared/services/auth.guard';
NodeLookupComponent,
BackupComponent,
ChannelBackupTableComponent,
QueryRoutesComponent,
ChannelRestoreTableComponent,
SignVerifyMessageComponent,
SignComponent,
VerifyComponent,
QueryRoutesComponent,
OnChainSendComponent,
OnChainReceiveComponent,
OnChainComponent,

View File

@ -10,10 +10,11 @@ import { LookupsComponent } from './lookups/lookups.component';
import { RoutingComponent } from './routing/routing.component';
import { OnChainComponent } from './on-chain/on-chain.component';
import { NetworkInfoComponent } from './network-info/network-info.component';
import { BackupComponent } from './backup/backup.component';
import { SignVerifyMessageComponent } from './sign-verify-message/sign-verify-message.component';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
import { AuthGuard, LNDUnlockedGuard } from '../shared/services/auth.guard';
import { NotFoundComponent } from '../shared/components/not-found/not-found.component';
import { BackupComponent } from './backup/backup.component';
export const LndRoutes: Routes = [
{ path: '', component: LNDRootComponent,
@ -23,6 +24,7 @@ export const LndRoutes: Routes = [
{ path: 'onchain', component: OnChainComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'peerschannels', component: PeersChannelsComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'transactions', component: TransactionsComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'signverify', component: SignVerifyMessageComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'backup', component: BackupComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'routing', component: RoutingComponent, canActivate: [LNDUnlockedGuard] },
{ path: 'lookups', component: LookupsComponent, canActivate: [LNDUnlockedGuard] },

View File

@ -1,5 +1,4 @@
import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, filter, take } from 'rxjs/operators';
import { Store } from '@ngrx/store';

View File

@ -0,0 +1,15 @@
<div fxLayout="row" fxLayoutAlign="start center" class="padding-gap-x page-title-container">
<fa-icon [icon]="faUserCheck" class="page-title-img mr-1"></fa-icon>
<span class="page-title">Sign/Verify Message</span>
</div>
<div fxLayout="column" class="padding-gap-x">
<mat-card>
<mat-card-content fxLayout="column">
<mat-tab-group>
<mat-tab label="Sign"><rtl-sign></rtl-sign></mat-tab>
<mat-tab label="Verify"><rtl-verify></rtl-verify></mat-tab>
</mat-tab-group>
</mat-card-content>
</mat-card>
</div>

View File

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

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { faUserCheck } from '@fortawesome/free-solid-svg-icons';
@Component({
selector: 'rtl-sign-verify-message',
templateUrl: './sign-verify-message.component.html',
styleUrls: ['./sign-verify-message.component.scss']
})
export class SignVerifyMessageComponent {
public faUserCheck = faUserCheck;
constructor() {}
}

View File

@ -0,0 +1,16 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #form="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<textarea autoFocus matInput [(ngModel)]="message" rows="5" (focus)="signature=''" placeholder="Message" required tabindex="1" name="message"></textarea>
<mat-error *ngIf="!message">Message is required.</mat-error>
</mat-form-field>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="mt-1 bordered-box padding-gap-large">
<p>{{signature}}</p>
</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="30" mat-stroked-button color="primary" tabindex="2" type="reset" (click)="resetData()" type="reset">Clear Field</button>
<button fxFlex="30" mat-flat-button color="primary" (click)="onSign()" tabindex="3" type="submit">Sign</button>
<button fxFlex="35" mat-flat-button color="primary" tabindex="4" rtlClipboard [payload]="signature" (copied)="onCopyField($event)" type="button">Copy Signature</button>
</div>
</form>
</div>

View File

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

View File

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

View File

@ -0,0 +1,44 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { MatSnackBar } from '@angular/material';
import { DataService } from '../../../shared/services/data.service';
import { LoggerService } from '../../../shared/services/logger.service';
@Component({
selector: 'rtl-sign',
templateUrl: './sign.component.html',
styleUrls: ['./sign.component.scss']
})
export class SignComponent implements OnInit, OnDestroy {
public message = '';
public signature = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private dataService: DataService, private snackBar: MatSnackBar, private logger: LoggerService) {}
ngOnInit() {}
onSign() {
if (!this.message || this.message === '') { return true; }
this.dataService.signMessage(this.message).pipe(takeUntil(this.unSubs[0])).subscribe(res => { this.signature = res; });
}
onCopyField(payload: string) {
this.snackBar.open('Signature copied.');
this.logger.info('Copied Text: ' + payload);
}
resetData() {
this.message = '';
this.signature = '';
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

View File

@ -0,0 +1,19 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<form fxLayout="column" fxLayoutAlign="space-between stretch" fxLayout.gt-sm="row wrap" #form="ngForm">
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<textarea autoFocus matInput [(ngModel)]="message" rows="5" (focus)="verifyRes=null" placeholder="Message" required tabindex="1" name="message"></textarea>
<mat-error *ngIf="!message">Message is required.</mat-error>
</mat-form-field>
<mat-form-field fxFlex="100" fxLayoutAlign="start end">
<input matInput placeholder="Signature" name="signature" [(ngModel)]="signature" tabindex="2" required #sign="ngModel">
<mat-error *ngIf="!signature">Signature is required.</mat-error>
</mat-form-field>
<div fxLayout="column" fxFlex="100" fxLayoutAlign="start start" class="mt-1 bordered-box padding-gap-large" [ngClass]="{'border-valid': verifyRes?.valid, 'border-invalid': verifyRes?.pubkey && !verifyRes?.valid}">
<p>{{!verifyRes?.pubkey ? '' : (verifyRes?.pubkey && verifyRes?.valid) ? 'Valid: ' : 'Invalid: '}}{{verifyRes?.pubkey}}</p>
</div>
<div fxLayout="row" fxFlex="100" fxFlex.gt-sm="30" fxLayoutAlign="space-between stretch" class="mt-2">
<button fxFlex="48" mat-stroked-button color="primary" tabindex="3" type="reset" (click)="resetData()" type="reset">Clear Field</button>
<button fxFlex="48" mat-flat-button color="primary" (click)="onVerify()" tabindex="4" type="submit">Verify</button>
</div>
</form>
</div>

View File

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

View File

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

View File

@ -0,0 +1,39 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { DataService } from '../../../shared/services/data.service';
@Component({
selector: 'rtl-verify',
templateUrl: './verify.component.html',
styleUrls: ['./verify.component.scss']
})
export class VerifyComponent implements OnInit, OnDestroy {
public message = '';
public signature = '';
public verifyRes = {pubkey: '', valid: null};
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
constructor(private dataService: DataService) {}
ngOnInit() {}
onVerify() {
if ((!this.message || this.message === '') || (!this.signature || this.signature === '')) { return true; }
this.dataService.verifyMessage(this.message, this.signature).pipe(takeUntil(this.unSubs[0])).subscribe(res => { this.verifyRes = res; });
}
resetData() {
this.message = '';
this.signature = '';
this.verifyRes = null;
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();
completeSub.complete();
});
}
}

View File

@ -9,7 +9,7 @@
<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">Closing channel: {{channelToClose.channel_point}}</p>
<p fxLayoutAlign="start center" class="pb-1 word-break">Closing channel: {{channelToClose.channel_point}}</p>
<div fxLayoutAlign="space-between center">
<mat-form-field fxFlex.gt-sm="48">
<mat-select [(value)]="selTransType" tabindex="1">

View File

@ -1,4 +1,4 @@
import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faTools, faProjectDiagram, faDownload, faServer, faPercentage } from '@fortawesome/free-solid-svg-icons';
import { faTachometerAlt, faLink, faBolt, faExchangeAlt, faUsers, faMapSigns, faQuestion, faSearch, faTools, faProjectDiagram, faDownload, faServer, faPercentage, faUserCheck } from '@fortawesome/free-solid-svg-icons';
import { UserPersonaEnum } from '../services/consts-enums-functions';
export const MENU_DATA: MenuRootNode = {
@ -10,7 +10,8 @@ export const MENU_DATA: MenuRootNode = {
{id: 32, parentId: 3, name: 'Transactions', iconType: 'FA', icon: faExchangeAlt, link: '/lnd/transactions', userPersona: UserPersonaEnum.ALL},
{id: 33, parentId: 3, name: 'Routing', iconType: 'FA', icon: faMapSigns, link: '/lnd/routing', userPersona: UserPersonaEnum.ALL},
{id: 34, parentId: 3, name: 'Graph Lookup', iconType: 'FA', icon: faSearch, link: '/lnd/lookups', userPersona: UserPersonaEnum.ALL},
{id: 35, parentId: 3, name: 'Backup', iconType: 'FA', icon: faDownload, link: '/lnd/backup', userPersona: UserPersonaEnum.ALL}
{id: 35, parentId: 3, name: 'Sign/Verify', iconType: 'FA', icon: faUserCheck, link: '/lnd/signverify', userPersona: UserPersonaEnum.ALL},
{id: 36, parentId: 3, name: 'Backup', iconType: 'FA', icon: faDownload, link: '/lnd/backup', userPersona: UserPersonaEnum.ALL}
]},
{id: 5, parentId: 0, name: 'Network', iconType: 'FA', icon: faProjectDiagram, link: '/lnd/network', userPersona: UserPersonaEnum.OPERATOR},
{id: 6, parentId: 0, name: 'Node/Network', iconType: 'FA', icon: faServer, link: '/lnd/network', userPersona: UserPersonaEnum.MERCHANT},

View File

@ -2,14 +2,21 @@ import { Injectable, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Subject, of, Observable } from 'rxjs';
import { take, map, takeUntil, catchError } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { LoggerService } from '../../shared/services/logger.service';
import { environment, API_URL } from '../../../environments/environment';
import * as RTLActions from '../../store/rtl.actions';
import * as fromRTLReducer from '../../store/rtl.reducers';
import { ErrorMessageComponent } from '../components/data-modal/error-message/error-message.component';
@Injectable()
export class DataService implements OnInit, OnDestroy {
private CHILD_API_URL = API_URL + '/lnd';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject()];
constructor(private httpClient: HttpClient) {}
constructor(private httpClient: HttpClient, private store: Store<fromRTLReducer.RTLState>, private logger: LoggerService,) {}
ngOnInit() {}
@ -29,6 +36,62 @@ export class DataService implements OnInit, OnDestroy {
return nodes$;
}
signMessage(msg: string) {
this.store.dispatch(new RTLActions.OpenSpinner('Signing Message...'));
return this.httpClient.post(this.CHILD_API_URL + environment.MESSAGE_API + '/sign', {message: msg})
.pipe(takeUntil(this.unSubs[1]),
map((res: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
return res.signature;
}),
catchError(err => {
this.handleErrorWithAlert('ERROR', 'Sign Message Failed', this.CHILD_API_URL + environment.MESSAGE_API + '/sign', err);
throw err;
}));
}
verifyMessage(msg: string, sign: string) {
this.store.dispatch(new RTLActions.OpenSpinner('Verifying Message...'));
return this.httpClient.post(this.CHILD_API_URL + environment.MESSAGE_API + '/verify', {message: msg, signature: sign})
.pipe(takeUntil(this.unSubs[2]),
map((res: any) => {
this.store.dispatch(new RTLActions.CloseSpinner());
return res;
}),
catchError(err => {
this.handleErrorWithAlert('ERROR', 'Verify Message Failed', this.CHILD_API_URL + environment.MESSAGE_API + '/verify', err);
throw err;
}));
}
handleErrorWithoutAlert(actionName: string, err: { status: number, error: any }) {
this.logger.error('ERROR IN: ' + actionName + '\n' + JSON.stringify(err));
if (err.status === 401) {
this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.Logout());
} else {
this.store.dispatch(new RTLActions.EffectErrorLnd({ action: actionName, code: err.status.toString(), message: err.error.error }));
}
}
handleErrorWithAlert(alertType: string, alertTitle: string, errURL: string, err: { status: number, error: any }) {
this.logger.error(err);
if (err.status === 401) {
this.logger.info('Redirecting to Login');
this.store.dispatch(new RTLActions.Logout());
} else {
this.store.dispatch(new RTLActions.CloseSpinner());
this.store.dispatch(new RTLActions.OpenAlert({
data: {
type: alertType,
alertTitle: alertTitle,
message: { code: err.status, message: err.error.error, URL: errURL },
component: ErrorMessageComponent
}
}));
}
}
ngOnDestroy() {
this.unSubs.forEach(completeSub => {
completeSub.next();

View File

@ -391,6 +391,7 @@
border: 1px solid $foreground-divider;
border-radius: 4px;
background: none;
min-height: 5.5rem;
}
.mat-expansion-panel {

View File

@ -961,3 +961,11 @@ table {
overflow-y: scroll;
max-height: 600px;
}
.border-valid {
border: 1px solid $green-color !important;
}
.border-invalid {
border: 1px solid $red-color !important;
}

View File

@ -22,5 +22,6 @@ export const environment = {
INVOICES_API: '/invoices',
SWITCH_API: '/switch',
ON_CHAIN_API: '/onchain',
MESSAGE_API: '/message',
VERSION: VERSION
};

View File

@ -22,5 +22,6 @@ export const environment = {
INVOICES_API: '/invoices',
SWITCH_API: '/switch',
ON_CHAIN_API: '/onchain',
MESSAGE_API: '/message',
VERSION: VERSION
};