Skip to content

Commit

Permalink
Implement estimateRouteFee #167
Browse files Browse the repository at this point in the history
  • Loading branch information
bnonni committed Jan 4, 2025
1 parent 0bdd2a7 commit 36ed141
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 0 deletions.
2 changes: 2 additions & 0 deletions lnd_methods/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const {diffieHellmanComputeSecret} = require('./signer');
const {disableChannel} = require('./offchain');
const {disconnectWatchtower} = require('./offchain');
const {enableChannel} = require('./offchain');
const {estimateRouteFee} = require('./offchain');
const {endGroupSigningSession} = require('./signer');
const {fundPendingChannels} = require('./onchain');
const {fundPsbt} = require('./onchain');
Expand Down Expand Up @@ -186,6 +187,7 @@ module.exports = {
disableChannel,
disconnectWatchtower,
enableChannel,
estimateRouteFee,
endGroupSigningSession,
fundPendingChannels,
fundPsbt,
Expand Down
32 changes: 32 additions & 0 deletions lnd_methods/offchain/estimate_route_fee.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {AuthenticatedLnd} from '../../lnd_grpc';
import {
AuthenticatedLightningArgs,
AuthenticatedLightningMethod
} from '../../typescript';

export type RouteFeeRequest = AuthenticatedLightningArgs<{
lnd: AuthenticatedLnd;
/** BOLT 11 Encoded Payment Request */
request: string;
/** Optional Timeout in Milliseconds */
timeout: number;
}>;

export type RouteFeeResponse = {
/** Millitokens (Routing Fee Msat) */
mtoken: number;
/** Timeout (Time Lock Delay) */
timeout: number;
};

/**
* Estimate routing fees based on an invoice.
*
* Requires `offchain:read` permission
*
* This method is not supported before LND 0.18.4
*/
export const estimateRouteFee: AuthenticatedLightningMethod<
RouteFeeRequest,
RouteFeeResponse
>;
83 changes: 83 additions & 0 deletions lnd_methods/offchain/estimate_route_fee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const asyncAuto = require('async/auto');
const {returnResult} = require('asyncjs-util');

/**
* Estimate routing fees based on an invoice.
*
* Requires `offchain:read` permission
*
* This method is not supported before LND 0.18.4
*
*
@argument
{
lnd: <Authenticated LND API Object>
request: <BOLT 11 Payment Request String>
timeout: <Optional Timeout in Milliseconds Number>
}
@returns via cbk or Promise
{
mtoken: <Route Fee Millitokens String>
timeout: <Time Lock Block Height Delay String>
}
*/
module.exports = ({lnd, request, timeout}, cbk) => {
return new Promise((resolve, reject) => {
return asyncAuto({
// Check arguments
validate: cbk => {
if (!lnd || !lnd.router) {
return cbk([400, 'ExpectedAuthenticatedLndForRouteFeeEstimate']);
}

if (!request) {
return cbk([400, 'ExpectedPaymentRequestStringForRouteFeeEstimate']);
}

if (timeout > 86400000) {
return cbk([400, 'ExpectedTimeoutLessThanOneDayForRouteFeeEstimate']);
}

if (timeout < 1) {
return cbk([400, 'ExpectedTimeoutGreaterThanZeroForRouteFeeEstimate']);
}

timeout = !timeout ? 60 : timeout / 1000;

return cbk();
},

// Estimate route fee and return a successful routing fee and timeout or failure reason
estimateRouteFee: ['validate', ({}, cbk) => {
return lnd.router.estimateRouteFee({request, timeout},
(err, res) => {
console.log('cbk', err, res)

if (err) {
return cbk([503, 'UnexpectedEstimateRouteFeeError', {err}]);
}

if (!res) {
return cbk([503, 'ExpectedRouteFeeResponse']);
}

if (!res.mtokens) {
return cbk([503, 'ExpectedRoutingFeeMtokensInRouteFeeResponse']);
}

if (!res.timeout) {
return cbk([503, 'ExpectedTimeLockDelayInRouteFeeResponse']);
}

if (res.failure_reason !== 'FAILURE_REASON_NONE') {
return cbk([404, 'EstimateRouteFindingFailed', {failure: res.failure_reason}])
}

return cbk(null, {mtokens: res.mtokens, timeout: res.timeout});
});
}],
},
returnResult({reject, resolve, of: 'estimateRouteFee'}, cbk));
});
};
1 change: 1 addition & 0 deletions lnd_methods/offchain/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './delete_pending_channel';
export * from './disable_channel';
export * from './disconnect_watchtower';
export * from './enable_channel';
export * from './estimate_route_fee';
export * from './get_backup';
export * from './get_backups';
export * from './get_channel_balance';
Expand Down
76 changes: 76 additions & 0 deletions test/lnd_methods/offchain/test_estimate_route_fee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
const {rejects, deepStrictEqual} = require('node:assert').strict;
const test = require('node:test');
const estimateRouteFee = require('./../../../lnd_methods/offchain/estimate_route_fee');

const request = 'lnbcrt500u1pnh3r5dpp57cppte59jvmxnaunh03ecy6wchq8e0zh70n0nzsamaxztqxevcusdqqcqzzsxqyz5vqsp587ua488ttsts8cs97ekt9axdla3jmq4mj2h7xj7g6rw37fu65yqs9qxpqysgql27u5p9m2xv0r0pjykzvcgs88azzfkywzw2xw5q6u86qnwzrut94mks86zxelhltdtn6vnqgd8hay433wwq7uvple709gp7pmwmtzwcqakyevc';

/** Function */
const makeLnd = ({err, res}) => {
const response = {
mtokens: '1050',
timeout: '3520',
failure_reason: 'FAILURE_REASON_NONE'
};
return {
router: {
estimateRouteFee: ({}, cbk) => cbk(err, res !== undefined ? res : response),
}
};
};

const makeArgs = override => {
const args = {request, timeout: 60000, lnd: makeLnd({})};
Object.keys(override || {}).forEach(key => args[key] = override[key]);
return args;
};

const tests = [
{
args: makeArgs({lnd: undefined}),
description: 'LND is required',
error: [400, 'ExpectedAuthenticatedLndForRouteFeeEstimate'],
},
{
args: makeArgs({request: undefined}),
description: 'Request is required',
error: [400, 'ExpectedPaymentRequestStringForRouteFeeEstimate'],
},
{
args: makeArgs({timeout: 86400001}),
description: 'Timeout must be less than or equal to 86400000',
error: [400, 'ExpectedTimeoutLessThanOneDayForRouteFeeEstimate'],
},
{
args: makeArgs({timeout: 0}),
description: 'Timeout must be greater than 0',
error: [400, 'ExpectedTimeoutGreaterThanZeroForRouteFeeEstimate'],
},
{
args: makeArgs({request, lnd: makeLnd({})}),
description: 'A route fee millitokens is expected for default timeout 60000ms',
expected: {
mtokens: '1050',
timeout: '3520'
}
},
{
args: makeArgs({request, timeout: 86400000, lnd: makeLnd({})}),
description: 'A route fee millitokens is expected for timeout 86400000ms',
expected: {
mtokens: '1050',
timeout: '3520'
}
}
];

tests.forEach(({args, description, error, expected}) => {
return test(description, async () => {
if (!!error) {
await rejects(estimateRouteFee(args), error, 'Got expected error');
} else {
deepStrictEqual(await estimateRouteFee(args), expected, 'Got result');
}

return;
});
});

0 comments on commit 36ed141

Please sign in to comment.