Skip to content

Commit

Permalink
feat: ✨ probe route (#63)
Browse files Browse the repository at this point in the history
  • Loading branch information
apotdevin authored Jun 6, 2020
1 parent 189495e commit 91628db
Show file tree
Hide file tree
Showing 36 changed files with 909 additions and 564 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"release:test": "standard-version --dry-run",
"release:minor": "standard-version --release-as minor && git push --follow-tags origin master",
"analyze": "cross-env ANALYZE=true next build",
"generate": "graphql-codegen --config codegen.yml",
"generate": "graphql-codegen --config codegen.yml && yarn lint",
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
Expand Down
3 changes: 3 additions & 0 deletions server/helpers/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ export const getAuthLnd = (auth: LndAuthType) => {
return lnd;
};

export const getLnd = (auth: AuthType, context: ContextType) =>
getAuthLnd(getCorrectAuth(auth, context));

export const getErrorMsg = (error: any[] | string): string => {
if (typeof error === 'string') {
return error;
Expand Down
4 changes: 2 additions & 2 deletions server/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import { routeResolvers } from './route/resolvers';
import { chainTypes } from './chain/types';
import { chainResolvers } from './chain/resolvers';
import { toolsResolvers } from './tools/resolvers';
import { toolsTypes } from './tools/types';
import { chatTypes } from './chat/types';
import { chatResolvers } from './chat/resolvers';
import { widgetResolvers } from './widgets/resolvers';
Expand All @@ -35,6 +34,7 @@ import { transactionTypes } from './transactions/types';
import { healthResolvers } from './health/resolvers';
import { healthTypes } from './health/types';
import { githubResolvers } from './github/resolvers';
import { routeTypes } from './route/types';

const typeDefs = [
generalTypes,
Expand All @@ -47,7 +47,6 @@ const typeDefs = [
bitcoinTypes,
peerTypes,
chainTypes,
toolsTypes,
chatTypes,
widgetTypes,
channelTypes,
Expand All @@ -56,6 +55,7 @@ const typeDefs = [
networkTypes,
transactionTypes,
healthTypes,
routeTypes,
];

const resolvers = merge(
Expand Down
167 changes: 63 additions & 104 deletions server/schema/invoice/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import { randomBytes, createHash } from 'crypto';
import {
payViaRoutes,
createInvoice,
pay as payRequest,
decodePaymentRequest,
payViaPaymentDetails,
parsePaymentRequest,
createInvoice as createInvoiceRequest,
} from 'ln-service';
import { ContextType } from 'server/types/apiTypes';
Expand All @@ -15,139 +13,82 @@ import {
getAuthLnd,
getErrorMsg,
getCorrectAuth,
getLnd,
} from 'server/helpers/helpers';
import { to } from 'server/helpers/async';

const KEYSEND_TYPE = '5482373484';

export const invoiceResolvers = {
Mutation: {
createInvoice: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'createInvoice');
Query: {
decodeRequest: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'decode');

const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
const lnd = getLnd(params.auth, context);

try {
const invoice = await createInvoiceRequest({
const decoded = await to(
decodePaymentRequest({
lnd,
tokens: params.amount,
});

return {
chainAddress: invoice.chain_address,
createdAt: invoice.created_at,
description: invoice.description,
id: invoice.id,
request: invoice.request,
secret: invoice.secret,
tokens: invoice.tokens,
};
} catch (error) {
logger.error('Error creating invoice: %o', error);
throw new Error(getErrorMsg(error));
}
},
parsePayment: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'parsePayment');

const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
request: params.request,
})
);

try {
const request = await parsePaymentRequest({
return {
...decoded,
destination_node: { lnd, publicKey: decoded.destination },
probe_route: {
lnd,
request: params.request,
});

const routes = request.routes.map(route => {
return {
mTokenFee: route.base_fee_mtokens,
channel: route.channel,
cltvDelta: route.cltv_delta,
feeRate: route.fee_rate,
publicKey: route.public_key,
};
});

return {
chainAddresses: request.chain_addresses,
cltvDelta: request.cltv_delta,
createdAt: request.created_at,
description: request.description,
descriptionHash: request.description_hash,
destination: request.destination,
expiresAt: request.expires_at,
id: request.id,
isExpired: request.is_expired,
mTokens: request.mtokens,
network: request.network,
routes,
tokens: request.tokens,
};
} catch (error) {
logger.error('Error decoding request: %o', error);
throw new Error(getErrorMsg(error));
}
destination: decoded.destination,
tokens: decoded.tokens,
},
};
},
pay: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'pay');
},
Mutation: {
createInvoice: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'createInvoice');

const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);

let isRequest = false;
try {
await decodePaymentRequest({
return await to(
createInvoiceRequest({
lnd,
request: params.request,
});
isRequest = true;
} catch (error) {
logger.error('Error decoding request: %o', error);
}

if (isRequest) {
try {
const payment = await payRequest({
lnd,
request: params.request,
});
return payment;
} catch (error) {
logger.error('Error paying request: %o', error);
throw new Error(getErrorMsg(error));
}
}
tokens: params.amount,
})
);
},
keysend: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'keysend');

if (!params.tokens) {
throw new Error('Amount of tokens is needed for keysend');
}
const { auth, destination, tokens } = params;
const lnd = getLnd(auth, context);

const preimage = randomBytes(32);
const secret = preimage.toString('hex');
const id = createHash('sha256').update(preimage).digest().toString('hex');

try {
const payment = await payViaPaymentDetails({
return await to(
payViaPaymentDetails({
id,
lnd,
tokens: params.tokens,
destination: params.request,
tokens,
destination,
messages: [
{
type: KEYSEND_TYPE,
value: secret,
},
],
});
return payment;
} catch (error) {
logger.error('Error paying request: %o', error);
throw new Error(getErrorMsg(error));
}
})
);
},
payViaRoute: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'payViaRoute');
circularRebalance: async (
_: undefined,
params: any,
context: ContextType
) => {
await requestLimiter(context.ip, 'circularRebalance');

const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
Expand All @@ -174,6 +115,24 @@ export const invoiceResolvers = {
throw new Error(getErrorMsg(error));
});

return true;
},
payViaRoute: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'payViaRoute');

const { auth, route: routeJSON, id } = params;
const lnd = getLnd(auth, context);

let route;
try {
route = JSON.parse(routeJSON);
} catch (error) {
logger.error('Corrupt route json: %o', error);
throw new Error('Corrupt Route JSON');
}

await to(payViaRoutes({ lnd, routes: [route], id }));

return true;
},
},
Expand Down
35 changes: 18 additions & 17 deletions server/schema/invoice/types.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import { gql } from 'apollo-server-micro';

export const invoiceTypes = gql`
type parsePaymentType {
chainAddresses: [String]
cltvDelta: Int
createdAt: DateTime
type decodeType {
chain_address: String
cltv_delta: Int
description: String
descriptionHash: String
description_hash: String
destination: String
expiresAt: DateTime
expires_at: String
id: String
isExpired: Boolean
mTokens: String
network: String
routes: [PaymentRouteType]
mtokens: String
payment: String
routes: [[RouteType]]
safe_tokens: Int
tokens: Int
destination_node: Node!
probe_route: ProbeRoute
}
type PaymentRouteType {
mTokenFee: String
type RouteType {
base_fee_mtokens: String
channel: String
cltvDelta: Int
feeRate: Int
publicKey: String
cltv_delta: Int
fee_rate: Int
public_key: String
}
type payType {
Expand All @@ -48,8 +49,8 @@ export const invoiceTypes = gql`
}
type newInvoiceType {
chainAddress: String
createdAt: DateTime
chain_address: String
created_at: DateTime
description: String
id: String
request: String
Expand Down
18 changes: 4 additions & 14 deletions server/schema/node/resolvers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from 'ln-service';
import { to, toWithError } from 'server/helpers/async';
import { requestLimiter } from 'server/helpers/rateLimiter';
import { getAuthLnd, getErrorMsg, getCorrectAuth } from '../../helpers/helpers';
import { getAuthLnd, getCorrectAuth, getLnd } from '../../helpers/helpers';
import { ContextType } from '../../types/apiTypes';
import { logger } from '../../helpers/logger';

Expand All @@ -16,20 +16,10 @@ export const nodeResolvers = {
getNode: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'closedChannels');

const auth = getCorrectAuth(params.auth, context);
const lnd = getAuthLnd(auth);
const { auth, withoutChannels = true, publicKey } = params;
const lnd = getLnd(auth, context);

try {
const nodeInfo = await getLnNode({
lnd,
is_omitting_channels: params.withoutChannels ?? true,
public_key: params.publicKey,
});
return nodeInfo;
} catch (error) {
logger.error('Error getting closed channels: %o', error);
throw new Error(getErrorMsg(error));
}
return { lnd, publicKey, withChannels: !withoutChannels };
},
getNodeInfo: async (_: undefined, params: any, context: ContextType) => {
await requestLimiter(context.ip, 'nodeInfo');
Expand Down
2 changes: 1 addition & 1 deletion server/schema/node/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { gql } from 'apollo-server-micro';

export const nodeTypes = gql`
type nodeType {
alias: String
alias: String!
capacity: String
channel_count: Int
color: String
Expand Down
Loading

0 comments on commit 91628db

Please sign in to comment.