sendPaymentV2 wrappers
This commit is contained in:
parent
0a82226e2c
commit
7543a7eca9
4 changed files with 452 additions and 4 deletions
1
utils/lightningServices/index.js
Normal file
1
utils/lightningServices/index.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./lightning-services')
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
/**
|
/**
|
||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
const FS = require('../utils/fs')
|
|
||||||
const lnrpc = require('../services/lnd/lightning')
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('commander').Command} Command
|
* @typedef {import('commander').Command} Command
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const FS = require('../../utils/fs')
|
||||||
|
const lnrpc = require('../../services/lnd/lightning')
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {object} Config
|
* @typedef {object} Config
|
||||||
* @prop {boolean} useTLS
|
* @prop {boolean} useTLS
|
||||||
|
|
@ -46,7 +46,7 @@ class LightningServices {
|
||||||
/**
|
/**
|
||||||
* @type {Config}
|
* @type {Config}
|
||||||
*/
|
*/
|
||||||
const newDefaults = require('../config/defaults')(program.mainnet)
|
const newDefaults = require('../../config/defaults')(program.mainnet)
|
||||||
|
|
||||||
this.defaults = newDefaults
|
this.defaults = newDefaults
|
||||||
|
|
||||||
108
utils/lightningServices/types.ts
Normal file
108
utils/lightningServices/types.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface PaymentV2 {
|
||||||
|
payment_hash: string
|
||||||
|
|
||||||
|
creation_date: string
|
||||||
|
|
||||||
|
payment_preimage: string
|
||||||
|
|
||||||
|
value_sat: string
|
||||||
|
|
||||||
|
value_msat: string
|
||||||
|
|
||||||
|
payment_request: string
|
||||||
|
|
||||||
|
status: 'UNKNOWN' | 'IN_FLIGHT' | 'SUCCEEDED' | 'FAILED'
|
||||||
|
|
||||||
|
fee_sat: string
|
||||||
|
|
||||||
|
fee_msat: string
|
||||||
|
|
||||||
|
creation_time_ns: string
|
||||||
|
|
||||||
|
payment_index: string
|
||||||
|
|
||||||
|
failure_reason:
|
||||||
|
| 'FAILURE_REASON_NONE'
|
||||||
|
| 'FAILURE_REASON_TIMEOUT'
|
||||||
|
| 'FAILURE_REASON_NO_ROUTE'
|
||||||
|
| 'FAILURE_REASON_ERROR'
|
||||||
|
| 'FAILURE_REASON_INCORRECT_PAYMENT_DETAILS'
|
||||||
|
| 'FAILURE_REASON_INSUFFICIENT_BALANCE'
|
||||||
|
}
|
||||||
|
|
||||||
|
enum FeatureBit {
|
||||||
|
DATALOSS_PROTECT_REQ = 0,
|
||||||
|
DATALOSS_PROTECT_OPT = 1,
|
||||||
|
INITIAL_ROUING_SYNC = 3,
|
||||||
|
UPFRONT_SHUTDOWN_SCRIPT_REQ = 4,
|
||||||
|
UPFRONT_SHUTDOWN_SCRIPT_OPT = 5,
|
||||||
|
GOSSIP_QUERIES_REQ = 6,
|
||||||
|
GOSSIP_QUERIES_OPT = 7,
|
||||||
|
TLV_ONION_REQ = 8,
|
||||||
|
TLV_ONION_OPT = 9,
|
||||||
|
EXT_GOSSIP_QUERIES_REQ = 10,
|
||||||
|
EXT_GOSSIP_QUERIES_OPT = 11,
|
||||||
|
STATIC_REMOTE_KEY_REQ = 12,
|
||||||
|
STATIC_REMOTE_KEY_OPT = 13,
|
||||||
|
PAYMENT_ADDR_REQ = 14,
|
||||||
|
PAYMENT_ADDR_OPT = 15,
|
||||||
|
MPP_REQ = 16,
|
||||||
|
MPP_OPT = 17
|
||||||
|
}
|
||||||
|
|
||||||
|
interface _SendPaymentV2Request {
|
||||||
|
dest: Buffer
|
||||||
|
/**
|
||||||
|
* Number of satoshis to send. The fields amt and amt_msat are mutually
|
||||||
|
* exclusive.
|
||||||
|
*/
|
||||||
|
amt: string
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CLTV delta from the current height that should be used to set the
|
||||||
|
* timelock for the final hop.
|
||||||
|
*/
|
||||||
|
final_cltv_delta: number
|
||||||
|
|
||||||
|
dest_features: FeatureBit[]
|
||||||
|
|
||||||
|
dest_custom_records: Record<number, Buffer>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash to use within the payment's HTLC.
|
||||||
|
*/
|
||||||
|
payment_hash: Buffer
|
||||||
|
|
||||||
|
max_parts: number
|
||||||
|
|
||||||
|
timeout_seconds: number
|
||||||
|
|
||||||
|
no_inflight_updates: boolean
|
||||||
|
|
||||||
|
payment_request: string
|
||||||
|
|
||||||
|
fee_limit_sat: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SendPaymentV2Request = Partial<_SendPaymentV2Request>
|
||||||
|
|
||||||
|
export interface SendPaymentKeysendParams {
|
||||||
|
amt: string
|
||||||
|
dest: string
|
||||||
|
feeLimit: string
|
||||||
|
finalCltvDelta?: number
|
||||||
|
maxParts?: number
|
||||||
|
timeoutSeconds?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SendPaymentInvoiceParams {
|
||||||
|
amt?: string
|
||||||
|
feeLimit: string
|
||||||
|
max_parts?: number
|
||||||
|
payment_request: string
|
||||||
|
timeoutSeconds?: number
|
||||||
|
}
|
||||||
339
utils/lightningServices/v2.js
Normal file
339
utils/lightningServices/v2.js
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
/**
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
const Crypto = require('crypto')
|
||||||
|
const logger = require('winston')
|
||||||
|
|
||||||
|
const lightningServices = require('./lightning-services')
|
||||||
|
/**
|
||||||
|
* @typedef {import('./types').PaymentV2} PaymentV2
|
||||||
|
* @typedef {import('./types').SendPaymentV2Request} SendPaymentV2Request
|
||||||
|
* @typedef {import('./types').SendPaymentInvoiceParams} SendPaymentInvoiceParams
|
||||||
|
* @typedef {import('./types').SendPaymentKeysendParams} SendPaymentKeysendParams
|
||||||
|
*/
|
||||||
|
|
||||||
|
//https://github.com/lightningnetwork/lnd/blob/master/record/experimental.go#L5:2
|
||||||
|
//might break in future updates
|
||||||
|
const KeySendType = 5482373484
|
||||||
|
//https://api.lightning.community/#featurebit
|
||||||
|
const TLV_ONION_REQ = 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SendPaymentV2Request} sendPaymentRequest
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isValidSendPaymentRequest = sendPaymentRequest => {
|
||||||
|
const {
|
||||||
|
amt,
|
||||||
|
dest,
|
||||||
|
dest_custom_records,
|
||||||
|
dest_features,
|
||||||
|
final_cltv_delta,
|
||||||
|
max_parts,
|
||||||
|
no_inflight_updates,
|
||||||
|
payment_hash,
|
||||||
|
timeout_seconds,
|
||||||
|
fee_limit_sat,
|
||||||
|
payment_request
|
||||||
|
} = sendPaymentRequest
|
||||||
|
|
||||||
|
if (typeof amt !== 'undefined' && typeof amt !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof dest !== 'undefined' && !(dest instanceof Buffer)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof dest_custom_records !== 'undefined' &&
|
||||||
|
typeof dest_custom_records !== 'object'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof dest_custom_records !== 'undefined' &&
|
||||||
|
dest_custom_records === null
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof dest_custom_records !== 'undefined' &&
|
||||||
|
Object.keys(dest_custom_records).length === 0
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof dest_features !== 'undefined' && !Array.isArray(dest_features)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof dest_features !== 'undefined' && dest_features.length === 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof final_cltv_delta !== 'undefined' &&
|
||||||
|
typeof final_cltv_delta !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof payment_hash !== 'undefined' &&
|
||||||
|
!(payment_hash instanceof Buffer)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof max_parts !== 'undefined' && typeof max_parts !== 'number') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof timeout_seconds !== 'undefined' &&
|
||||||
|
typeof timeout_seconds !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof no_inflight_updates !== 'undefined' &&
|
||||||
|
typeof no_inflight_updates !== 'boolean'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof fee_limit_sat !== 'undefined' &&
|
||||||
|
typeof fee_limit_sat !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof payment_request !== 'undefined' &&
|
||||||
|
typeof payment_request !== 'string'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SendPaymentKeysendParams} sendPaymentKeysendParams
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
const isValidSendPaymentKeysendParams = sendPaymentKeysendParams => {
|
||||||
|
const {
|
||||||
|
amt,
|
||||||
|
dest,
|
||||||
|
feeLimit,
|
||||||
|
finalCltvDelta,
|
||||||
|
maxParts,
|
||||||
|
timeoutSeconds
|
||||||
|
} = sendPaymentKeysendParams
|
||||||
|
|
||||||
|
if (typeof amt !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof dest !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof feeLimit !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof finalCltvDelta !== 'undefined' &&
|
||||||
|
typeof finalCltvDelta !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof maxParts !== 'undefined' && typeof maxParts !== 'number') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof timeoutSeconds !== 'undefined' &&
|
||||||
|
typeof timeoutSeconds !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SendPaymentInvoiceParams} sendPaymentInvoiceParams
|
||||||
|
*/
|
||||||
|
const isValidSendPaymentInvoiceParams = sendPaymentInvoiceParams => {
|
||||||
|
const {
|
||||||
|
amt,
|
||||||
|
feeLimit,
|
||||||
|
max_parts,
|
||||||
|
payment_request,
|
||||||
|
timeoutSeconds
|
||||||
|
} = sendPaymentInvoiceParams
|
||||||
|
|
||||||
|
// payment_request: string
|
||||||
|
// timeoutSeconds?: number
|
||||||
|
|
||||||
|
if (typeof amt !== 'undefined' && typeof amt !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof feeLimit !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof max_parts !== 'undefined' && typeof max_parts !== 'number') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof payment_request !== 'string') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof timeoutSeconds !== 'undefined' &&
|
||||||
|
typeof timeoutSeconds !== 'number'
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* aklssjdklasd
|
||||||
|
* @param {SendPaymentV2Request} sendPaymentRequest
|
||||||
|
* @returns {Promise<PaymentV2>}
|
||||||
|
*/
|
||||||
|
const sendPaymentV2 = sendPaymentRequest => {
|
||||||
|
const {
|
||||||
|
services: { router }
|
||||||
|
} = lightningServices
|
||||||
|
|
||||||
|
if (!isValidSendPaymentRequest(sendPaymentRequest)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid SendPaymentRequest: ${JSON.stringify(sendPaymentRequest)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {SendPaymentV2Request}
|
||||||
|
*/
|
||||||
|
const paymentRequest = {
|
||||||
|
...sendPaymentRequest,
|
||||||
|
no_inflight_updates: true
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
const stream = router.sendPaymentV2(paymentRequest)
|
||||||
|
|
||||||
|
stream.on(
|
||||||
|
'data',
|
||||||
|
/**
|
||||||
|
* @param {import("./types").PaymentV2} streamingPaymentV2
|
||||||
|
*/ streamingPaymentV2 => {
|
||||||
|
if (streamingPaymentV2.failure_reason !== 'FAILURE_REASON_NONE') {
|
||||||
|
rej(new Error(streamingPaymentV2.failure_reason))
|
||||||
|
} else {
|
||||||
|
res(streamingPaymentV2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// @ts-expect-error
|
||||||
|
stream.on('status', status => {
|
||||||
|
logger.info('SendPaymentV2 Status:', status)
|
||||||
|
})
|
||||||
|
|
||||||
|
stream.on(
|
||||||
|
'error',
|
||||||
|
/**
|
||||||
|
* @param {{ details: any; }} err
|
||||||
|
*/ err => {
|
||||||
|
logger.error('SendPaymentV2 Error:', err)
|
||||||
|
|
||||||
|
rej(err.details || err)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SendPaymentKeysendParams} params
|
||||||
|
* @returns {Promise<PaymentV2>}
|
||||||
|
*/
|
||||||
|
const sendPaymentV2Keysend = params => {
|
||||||
|
const {
|
||||||
|
amt,
|
||||||
|
dest,
|
||||||
|
feeLimit,
|
||||||
|
finalCltvDelta,
|
||||||
|
maxParts,
|
||||||
|
timeoutSeconds
|
||||||
|
} = params
|
||||||
|
|
||||||
|
if (!isValidSendPaymentKeysendParams(params)) {
|
||||||
|
throw new TypeError('Invalid SendPaymentKeysendParams')
|
||||||
|
}
|
||||||
|
|
||||||
|
const preimage = Crypto.randomBytes(32)
|
||||||
|
const r_hash = Crypto.createHash('sha256')
|
||||||
|
.update(preimage)
|
||||||
|
.digest()
|
||||||
|
|
||||||
|
return sendPaymentV2({
|
||||||
|
dest: Buffer.from(dest, 'hex'),
|
||||||
|
amt,
|
||||||
|
final_cltv_delta: finalCltvDelta,
|
||||||
|
dest_features: [TLV_ONION_REQ],
|
||||||
|
dest_custom_records: {
|
||||||
|
[KeySendType]: preimage
|
||||||
|
},
|
||||||
|
payment_hash: r_hash,
|
||||||
|
max_parts: maxParts,
|
||||||
|
timeout_seconds: timeoutSeconds,
|
||||||
|
fee_limit_sat: feeLimit
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SendPaymentInvoiceParams} params
|
||||||
|
* @returns {Promise<PaymentV2>}
|
||||||
|
*/
|
||||||
|
const sendPaymentV2Invoice = params => {
|
||||||
|
const {
|
||||||
|
feeLimit,
|
||||||
|
payment_request,
|
||||||
|
amt,
|
||||||
|
max_parts = 3,
|
||||||
|
timeoutSeconds = 5
|
||||||
|
} = params
|
||||||
|
|
||||||
|
if (!isValidSendPaymentInvoiceParams(params)) {
|
||||||
|
throw new TypeError('Invalid SendPaymentInvoiceParams')
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendPaymentV2({
|
||||||
|
amt,
|
||||||
|
payment_request,
|
||||||
|
fee_limit_sat: feeLimit,
|
||||||
|
max_parts,
|
||||||
|
timeout_seconds: timeoutSeconds
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
sendPaymentV2Keysend,
|
||||||
|
sendPaymentV2Invoice
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue