Skip to content

Commit

Permalink
cln: Bump fee
Browse files Browse the repository at this point in the history
  • Loading branch information
ShahanaFarooqui committed May 10, 2024
1 parent 01db38a commit 964e85c
Show file tree
Hide file tree
Showing 16 changed files with 186 additions and 23 deletions.
24 changes: 24 additions & 0 deletions backend/controllers/shared/RTLConf.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@ const logger = Logger;
const common = Common;
const wsServer = WSServer;
const databaseService = Database;
export const getExplorerFeesRecommended = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Recommended Fee Rates..' });
options.url = 'https://mempool.space/api/v1/fees/recommended';
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Recommended Fee Rates Received', data: body });
res.status(200).json(JSON.parse(body));
}).catch((errRes) => {
const errMsg = 'Get Recommended Fee Rates Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
});
};
export const getExplorerTransaction = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Transaction From Block Explorer..' });
options.url = 'https://mempool.space/api/tx/' + req.params.txid;
request(options).then((body) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Transaction From Block Explorer Received', data: body });
res.status(200).json(JSON.parse(body));
}).catch((errRes) => {
const errMsg = 'Get Transaction From Block Explorer Error';
const err = common.handleError({ statusCode: 500, message: errMsg, error: errRes }, 'RTLConf', errMsg, req.session.selectedNode);
return res.status(err.statusCode).json({ message: err.error, error: err.error });
});
};
export const getCurrencyRates = (req, res, next) => {
logger.log({ selectedNode: req.session.selectedNode, level: 'INFO', fileName: 'RTLConf', msg: 'Getting Currency Rates..' });
options.url = 'https://blockchain.info/ticker';
Expand Down
4 changes: 3 additions & 1 deletion backend/routes/shared/RTLConf.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import exprs from 'express';
const { Router } = exprs;
import { isAuthenticated } from '../../utils/authCheck.js';
import { updateNodeSettings, getConfig, getFile, updateSelectedNode, updateApplicationSettings, getCurrencyRates, getApplicationSettings } from '../../controllers/shared/RTLConf.js';
import { updateNodeSettings, getConfig, getFile, updateSelectedNode, updateApplicationSettings, getCurrencyRates, getApplicationSettings, getExplorerFeesRecommended, getExplorerTransaction } from '../../controllers/shared/RTLConf.js';
const router = Router();
router.get('/', getApplicationSettings);
router.get('/rates', getCurrencyRates);
Expand All @@ -10,4 +10,6 @@ router.get('/updateSelNode/:currNodeIndex/:prevNodeIndex', updateSelectedNode);
router.get('/config/:nodeType', isAuthenticated, getConfig);
router.post('/node', isAuthenticated, updateNodeSettings);
router.post('/application', isAuthenticated, updateApplicationSettings);
router.get('/explorerFeesRecommended', getExplorerFeesRecommended);
router.get('/explorerTransaction/:txid', getExplorerTransaction);
export default router;
1 change: 0 additions & 1 deletion frontend/125.05cbc1feb7174532.js

This file was deleted.

1 change: 1 addition & 0 deletions frontend/125.df5cc04df4994af5.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/index.html

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions frontend/main.5525c82b788903bc.js

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion frontend/main.6a8227167750bd90.js

This file was deleted.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,17 @@
<div fxLayout="column" fxFlex="100" fxLayoutAlign="space-between stretch">
<div fxFlex="100" class="alert alert-info">
<fa-icon class="mr-1 alert-icon" [icon]="faInfoCircle" />
<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 fxLayout="column" fxFlex="100">
<div>Fee rates recommended by mempool.space (sat/vByte):</div>
<div>- High: {{recommendedFee.fastestFee || 'Unknown'}}</div>
<div>- Medium: {{recommendedFee.halfHourFee || 'Unknown'}}</div>
<div>- Low: {{recommendedFee.hourFee || 'Unknown'}}</div>
</span>
</div>
<div *ngIf="flgShowDustWarning" fxFlex="100" class="alert alert-warn">
<fa-icon class="mr-1 alert-icon" [icon]="faExclamationTriangle" />
<span>Change output balance <strong>{{dustOutputValue | number}}</strong> (Sats) may be insufficient for fee bumping, depending on the prevailing fee rates.</span>
</div>
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between center">
<mat-form-field fxLayout="column" fxFlex="49">
<mat-label>Output Index</mat-label>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { faCopy, faInfoCircle, faExclamationTriangle } from '@fortawesome/free-s
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatSnackBar } from '@angular/material/snack-bar';

import { RecommendedFeeRates, BlockExplorerTransaction } from '../../../../shared/models/rtlModels';
import { Channel } from '../../../../shared/models/clnModels';
import { CLNChannelInformation } from '../../../../shared/models/alertData';
import { ADDRESS_TYPES, APICallStatusEnum, CLNActions } from '../../../../shared/services/consts-enums-functions';
import { LoggerService } from '../../../../shared/services/logger.service';
import { DataService } from '../../../../shared/services/data.service';

import { RTLState } from '../../../../store/rtl.state';
import { openSnackBar } from '../../../../store/rtl.actions';
Expand Down Expand Up @@ -39,12 +41,32 @@ export class CLNBumpFeeComponent implements OnInit, OnDestroy {
public faInfoCircle = faInfoCircle;
public faExclamationTriangle = faExclamationTriangle;
public bumpFeeError = '';
private unSubs: Array<Subject<void>> = [new Subject(), new Subject()];
public flgShowDustWarning = false;
public dustOutputValue = 0;
public recommendedFee = { fastestFee: 0, halfHourFee: 0, hourFee: 0 };
private unSubs: Array<Subject<void>> = [new Subject(), new Subject(), new Subject(), new Subject()];

constructor(private actions: Actions, public dialogRef: MatDialogRef<CLNBumpFeeComponent>, @Inject(MAT_DIALOG_DATA) public data: CLNChannelInformation, private store: Store<RTLState>, private logger: LoggerService, private snackBar: MatSnackBar) { }
constructor(private actions: Actions, public dialogRef: MatDialogRef<CLNBumpFeeComponent>, @Inject(MAT_DIALOG_DATA) public data: CLNChannelInformation, private store: Store<RTLState>, private logger: LoggerService, private dataService: DataService, private snackBar: MatSnackBar) { }

ngOnInit() {
this.bumpFeeChannel = this.data.channel;
this.dataService.getRecommendedFeeRates().pipe(takeUntil(this.unSubs[0])).subscribe({
next: (rfRes: RecommendedFeeRates) => {
this.recommendedFee = rfRes;
}, error: (err) => {
this.logger.error(err);
}
});
this.dataService.getBlockExplorerTransaction(this.bumpFeeChannel.funding_txid).
pipe(takeUntil(this.unSubs[1])).subscribe({
next: (txRes: BlockExplorerTransaction) => {
this.outputIndex = txRes.vout.findIndex((vout) => vout.value === this.bumpFeeChannel.to_us_msat) === 0 ? 1 : 0;
this.dustOutputValue = txRes.vout[this.outputIndex].value;
this.flgShowDustWarning = this.dustOutputValue < 1000;
}, error: (err) => {
this.logger.error(err);
}
});
}

onBumpFee(): boolean | void {
Expand All @@ -69,7 +91,7 @@ export class CLNBumpFeeComponent implements OnInit, OnDestroy {
this.store.dispatch(openSnackBar({ payload: 'Successfully bumped the fee. Use the block explorer to verify transaction.' }));
this.dialogRef.close();
});
this.actions.pipe(filter((action) => action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN), takeUntil(this.unSubs[0])).
this.actions.pipe(filter((action) => action.type === CLNActions.UPDATE_API_CALL_STATUS_CLN), takeUntil(this.unSubs[2])).
subscribe((action: any) => {
if (action.payload.status === APICallStatusEnum.ERROR && (action.payload.action === 'SetChannelTransaction' || action.payload.action === 'GenerateNewAddress')) {
this.logger.error(action.payload.message);
Expand All @@ -86,6 +108,7 @@ export class CLNBumpFeeComponent implements OnInit, OnDestroy {
this.bumpFeeError = '';
this.fees = null;
this.outputIndex = null;
this.flgShowDustWarning = false;
this.outputIdx.control.setErrors(null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<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">
<div fxLayout="row" fxFlex="100" fxLayoutAlign="space-between start">
<mat-form-field fxLayout="column" fxFlex.gt-sm="32" fxLayoutAlign="start end">
<mat-label>Index for Change Output</mat-label>
<input #outputIdx="ngModel" matInput type="number" tabindex="1" required name="outputIdx" [step]="1" [min]="0" [(ngModel)]="outputIndex">
Expand Down
52 changes: 46 additions & 6 deletions src/app/shared/models/rtlModels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,50 @@ export interface FiatCurrency {
}

export interface ConvertedCurrency {
unit: string;
iconType: 'FA' | 'SVG';
symbol: string | IconDefinition | SafeHtml;
Sats: number;
BTC: number;
OTHER: number;
unit: string;
iconType: 'FA' | 'SVG';
symbol: string | IconDefinition | SafeHtml;
Sats: number;
BTC: number;
OTHER: number;
};

export interface RecommendedFeeRates {
fastestFee: number;
halfHourFee: number;
hourFee: number;
economyFee?: number;
minimumFee?: number;
};

export interface BETransactionVOut {
value: number;
scriptpubkey?: string;
scriptpubkey_asm?: string;
scriptpubkey_type?: string;
scriptpubkey_address?: string;
}

export interface BETransactionVIn {
txid: string;
vout?: string;
prevout?: BETransactionVOut;
scriptsig?: string;
scriptsig_asm?: string;
witness?: string[];
is_coinbase?: boolean;
sequence?: number;
}

export interface BlockExplorerTransaction {
txid: string;
version?: number;
locktime?: number;
size?: number;
weight?: number;
sigops?: number;
fee?: number;
status?: any;
vin?: BETransactionVIn[];
vout?: BETransactionVOut[];
}
8 changes: 8 additions & 0 deletions src/app/shared/services/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ export class DataService implements OnDestroy {
this.lnImplementationUpdated.next(this.lnImplementation);
}

getRecommendedFeeRates() {
return this.httpClient.get(API_END_POINTS.CONF_API + '/explorerFeesRecommended');
}

getBlockExplorerTransaction(txid: string) {
return this.httpClient.get(API_END_POINTS.CONF_API + '/explorerTransaction/' + txid);
}

getFiatRates() {
return this.httpClient.get(API_END_POINTS.CONF_API + '/rates');
}
Expand Down
8 changes: 8 additions & 0 deletions src/app/shared/test-helpers/mock-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ export class mockDataService {
this.lnImplementationUpdated.next(this.lnImplementation);
}

getRecommendedFeeRates() {
return of(mockResponseData.blockExplorerRecommendedFee);
}

getBlockExplorerTransaction(txid: string) {
return of(mockResponseData.blockExplorerTransaction);
}

getFiatRates() {
return of(mockResponseData.fiatRates);
}
Expand Down
56 changes: 56 additions & 0 deletions src/app/shared/test-helpers/test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,62 @@ export const mockResponseData = {
symbol: 'TWD'
}
},
blockExplorerRecommendedFee: {
fastestFee: 19,
halfHourFee: 19,
hourFee: 19,
economyFee: 19,
minimumFee: 10
},
blockExplorerTransaction: {
txid: '44a33c87c35fb21da5140286b91b56489b9ffaf7f62bcd0cce30ab568a0129ae',
version: 1,
locktime: 0,
vin: [
{
txid: 'a41831699ce28f98e75b333268c6f2b1703a77748c49d1aa914a3c2d538077cb',
vout: 0,
prevout: {
scriptpubkey: '0014378b753631273729605c694452ff89735dca7c19',
scriptpubkey_asm: 'OP_0 OP_PUSHBYTES_20 378b753631273729605c694452ff89735dca7c19',
scriptpubkey_type: 'v0_p2wpkh',
scriptpubkey_address: 'bc1qx79h2d33yumjjczud9z99lufwdwu5lqephsey4',
value: 1436617
},
scriptsig: '',
scriptsig_asm: '',
witness: [
'3045022100936bf7c13a6b349f59b7824f98da84f271c16d4eb8c8cf4933ce8921ba45ab00022015daa05a9430a2647d7deb86e13f71e7534538e3f7b6e8af70ffaec6a7b74dda01',
'0227b8e44fb3deac91671c648707b575ce761dc35905d09a4ac1be31198ad23eef'
],
is_coinbase: false,
sequence: 4294967293
}
],
vout: [
{
scriptpubkey: '0014f8ec16db533ed1147d638e9425ad330d4d0cb1ce',
scriptpubkey_asm: 'OP_0 OP_PUSHBYTES_20 f8ec16db533ed1147d638e9425ad330d4d0cb1ce',
scriptpubkey_type: 'v0_p2wpkh',
scriptpubkey_address: 'bc1qlrkpdk6n8mg3gltr362zttfnp4xsevww8qc8md',
value: 1350761
},
{
scriptpubkey: '76a9145af54c7385fe38521bddf13cd78486a344fcae1c88ac',
scriptpubkey_asm: 'OP_DUP OP_HASH160 OP_PUSHBYTES_20 5af54c7385fe38521bddf13cd78486a344fcae1c OP_EQUALVERIFY OP_CHECKSIG',
scriptpubkey_type: 'p2pkh',
scriptpubkey_address: '19HwfuwwZUMTgvUEmoic2teLh7RU7L1HHf',
value: 84128
}
],
size: 226,
weight: 574,
sigops: 5,
fee: 1728,
status: {
confirmed: false
}
},
decodePayment: {
destination: '031844beb16bf8dd8c7bc30588b8c37b36e62b71c6e812e9b6d976c0a57e151be2',
payment_hash: 'a53968453af7ab6fc58d229a91bdf23d7c121963067f06cf02e1a7b581852c07',
Expand Down

0 comments on commit 964e85c

Please sign in to comment.