You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
7.7 KiB
321 lines
7.7 KiB
import * as ByteBuffer from 'bytebuffer'
|
|
import * as fs from 'fs'
|
|
import * as grpc from 'grpc'
|
|
import { sleep } from '../helpers';
|
|
import * as sha from 'js-sha256'
|
|
import * as crypto from 'crypto'
|
|
// var protoLoader = require('@grpc/proto-loader')
|
|
const env = process.env.NODE_ENV || 'development';
|
|
const config = require(__dirname + '/../../config/app.json')[env];
|
|
|
|
const LND_KEYSEND_KEY = 5482373484
|
|
const SPHINX_CUSTOM_RECORD_KEY = 133773310
|
|
|
|
var lightningClient = <any> null;
|
|
var walletUnlocker = <any> null;
|
|
|
|
const loadCredentials = () => {
|
|
var lndCert = fs.readFileSync(config.tls_location);
|
|
var sslCreds = grpc.credentials.createSsl(lndCert);
|
|
var m = fs.readFileSync(config.macaroon_location);
|
|
var macaroon = m.toString('hex');
|
|
var metadata = new grpc.Metadata()
|
|
metadata.add('macaroon', macaroon)
|
|
var macaroonCreds = grpc.credentials.createFromMetadataGenerator((_args, callback) => {
|
|
callback(null, metadata);
|
|
});
|
|
|
|
return grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds);
|
|
}
|
|
|
|
// async function loadLightningNew() {
|
|
// if (lightningClient) {
|
|
// return lightningClient
|
|
// } else {
|
|
// var credentials = loadCredentials()
|
|
// const packageDefinition = await protoLoader.load("rpc.proto", {})
|
|
// const lnrpcDescriptor = grpc.loadPackageDefinition(packageDefinition);
|
|
// var { lnrpc } = lnrpcDescriptor;
|
|
// lightningClient = new lnrpc.Lightning(config.node_ip + ':' + config.lnd_port, credentials);
|
|
// return lightningClient
|
|
// }
|
|
// }
|
|
|
|
const loadLightning = () => {
|
|
if (lightningClient) {
|
|
return lightningClient
|
|
} else {
|
|
try{
|
|
var credentials = loadCredentials()
|
|
var lnrpcDescriptor = grpc.load("rpc.proto");
|
|
var lnrpc: any = lnrpcDescriptor.lnrpc
|
|
lightningClient = new lnrpc.Lightning(config.node_ip + ':' + config.lnd_port, credentials);
|
|
return lightningClient
|
|
} catch(e) {
|
|
throw e
|
|
}
|
|
}
|
|
}
|
|
|
|
const loadWalletUnlocker = () => {
|
|
if (walletUnlocker) {
|
|
return walletUnlocker
|
|
} else {
|
|
var credentials = loadCredentials()
|
|
try{
|
|
var lnrpcDescriptor = grpc.load("rpc.proto");
|
|
var lnrpc: any = lnrpcDescriptor.lnrpc
|
|
walletUnlocker = new lnrpc.WalletUnlocker(config.node_ip + ':' + config.lnd_port, credentials);
|
|
return walletUnlocker
|
|
} catch(e) {
|
|
console.log(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
const getHeaders = (req) => {
|
|
return {
|
|
"X-User-Token": req.headers['x-user-token'],
|
|
"X-User-Email": req.headers['x-user-email']
|
|
}
|
|
}
|
|
|
|
var isLocked = false
|
|
let lockTimeout: ReturnType<typeof setTimeout>;
|
|
const getLock = () => isLocked
|
|
const setLock = (value) => {
|
|
isLocked = value
|
|
console.log({ isLocked })
|
|
if (lockTimeout) clearTimeout(lockTimeout)
|
|
lockTimeout = setTimeout(() => {
|
|
isLocked = false
|
|
console.log({ isLocked })
|
|
}, 1000 * 60 * 2)
|
|
}
|
|
|
|
const getRoute = async (pub_key, amt, callback) => {
|
|
let lightning = await loadLightning()
|
|
lightning.queryRoutes(
|
|
{ pub_key, amt },
|
|
(err, response) => callback(err, response)
|
|
)
|
|
}
|
|
|
|
const keysend = (opts) => {
|
|
return new Promise(async function(resolve, reject) {
|
|
let lightning = await loadLightning()
|
|
|
|
const randoStr = crypto.randomBytes(32).toString('hex');
|
|
const preimage = ByteBuffer.fromHex(randoStr)
|
|
const options = {
|
|
amt: opts.amt,
|
|
final_cltv_delta: 10,
|
|
dest: ByteBuffer.fromHex(opts.dest),
|
|
dest_custom_records: {
|
|
[`${LND_KEYSEND_KEY}`]: preimage,
|
|
[`${SPHINX_CUSTOM_RECORD_KEY}`]: ByteBuffer.fromUTF8(opts.data),
|
|
},
|
|
payment_hash: sha.sha256.arrayBuffer(preimage.toBuffer()),
|
|
dest_features:[9],
|
|
}
|
|
const call = lightning.sendPayment()
|
|
call.on('data', function(payment) {
|
|
if(payment.payment_error){
|
|
reject(payment.payment_error)
|
|
} else {
|
|
resolve(payment)
|
|
}
|
|
})
|
|
call.on('error', function(err) {
|
|
reject(err)
|
|
})
|
|
call.write(options)
|
|
})
|
|
}
|
|
|
|
const MAX_MSG_LENGTH = 972 // 1146 - 20
|
|
async function keysendMessage(opts) {
|
|
return new Promise(async function(resolve, reject) {
|
|
if(!opts.data || typeof opts.data!=='string') {
|
|
return reject('string plz')
|
|
}
|
|
if(opts.data.length<MAX_MSG_LENGTH){
|
|
try {
|
|
const res = await keysend(opts)
|
|
resolve(res)
|
|
} catch(e) {
|
|
reject(e)
|
|
}
|
|
return
|
|
}
|
|
// too long! need to send serial
|
|
const n = Math.ceil(opts.data.length / MAX_MSG_LENGTH)
|
|
let success = false
|
|
let fail = false
|
|
let res:any = null
|
|
const ts = new Date().valueOf()
|
|
await asyncForEach(Array.from(Array(n)), async(u,i)=> {
|
|
const spliti = Math.ceil(opts.data.length/n)
|
|
const m = opts.data.substr(i*spliti, spliti)
|
|
try {
|
|
res = await keysend({...opts,
|
|
data: `${ts}_${i}_${n}_${m}`
|
|
})
|
|
success = true
|
|
await sleep(432)
|
|
} catch(e) {
|
|
console.log(e)
|
|
fail = true
|
|
}
|
|
})
|
|
if(success && !fail) {
|
|
resolve(res)
|
|
} else {
|
|
reject(new Error('fail'))
|
|
}
|
|
})
|
|
}
|
|
|
|
async function asyncForEach(array, callback) {
|
|
for (let index = 0; index < array.length; index++) {
|
|
await callback(array[index], index, array);
|
|
}
|
|
}
|
|
|
|
async function signAscii(ascii) {
|
|
try {
|
|
const sig = await signMessage(ascii_to_hexa(ascii))
|
|
return sig
|
|
} catch(e) {
|
|
throw e
|
|
}
|
|
}
|
|
|
|
function listInvoices() {
|
|
return new Promise(async(resolve, reject)=> {
|
|
const lightning = await loadLightning()
|
|
lightning.listInvoices({
|
|
num_max_invoices:100000,
|
|
reversed:true,
|
|
}, (err, response) => {
|
|
if(!err) {
|
|
resolve(response)
|
|
} else {
|
|
reject(err)
|
|
}
|
|
});
|
|
})
|
|
}
|
|
|
|
function listPayments() {
|
|
return new Promise(async(resolve, reject)=> {
|
|
const lightning = await loadLightning()
|
|
lightning.listPayments({}, (err, response) => {
|
|
if(!err) {
|
|
resolve(response)
|
|
} else {
|
|
reject(err)
|
|
}
|
|
});
|
|
})
|
|
}
|
|
|
|
const signMessage = (msg) => {
|
|
return new Promise(async(resolve, reject)=> {
|
|
let lightning = await loadLightning()
|
|
try {
|
|
const options = {msg:ByteBuffer.fromHex(msg)}
|
|
lightning.signMessage(options, function(err,sig){
|
|
if(err || !sig.signature) {
|
|
reject(err)
|
|
} else {
|
|
resolve(sig.signature)
|
|
}
|
|
})
|
|
} catch(e) {
|
|
reject(e)
|
|
}
|
|
})
|
|
}
|
|
|
|
const signBuffer = (msg) => {
|
|
return new Promise(async (resolve, reject)=> {
|
|
let lightning = await loadLightning()
|
|
try {
|
|
const options = {msg}
|
|
lightning.signMessage(options, function(err,sig){
|
|
if(err || !sig.signature) {
|
|
reject(err)
|
|
} else {
|
|
resolve(sig.signature)
|
|
}
|
|
})
|
|
} catch(e) {
|
|
reject(e)
|
|
}
|
|
})
|
|
}
|
|
|
|
const verifyMessage = (msg,sig) => {
|
|
return new Promise(async(resolve, reject)=> {
|
|
let lightning = await loadLightning()
|
|
try {
|
|
const options = {
|
|
msg:ByteBuffer.fromHex(msg),
|
|
signature:sig,
|
|
}
|
|
console.log(options)
|
|
lightning.verifyMessage(options, function(err,res){
|
|
if(err || !res.pubkey) {
|
|
reject(err)
|
|
} else {
|
|
resolve(res)
|
|
}
|
|
})
|
|
} catch(e) {
|
|
reject(e)
|
|
}
|
|
})
|
|
}
|
|
|
|
async function checkConnection(){
|
|
return new Promise((resolve,reject)=>{
|
|
const lightning = loadLightning()
|
|
lightning.getInfo({}, function(err, response) {
|
|
if (err == null) {
|
|
resolve(response)
|
|
} else {
|
|
reject(err)
|
|
}
|
|
});
|
|
})
|
|
}
|
|
|
|
function ascii_to_hexa(str){
|
|
var arr1 = <string[]> [];
|
|
for (var n = 0, l = str.length; n < l; n ++) {
|
|
var hex = Number(str.charCodeAt(n)).toString(16);
|
|
arr1.push(hex);
|
|
}
|
|
return arr1.join('');
|
|
}
|
|
|
|
export {
|
|
loadCredentials,
|
|
loadLightning,
|
|
loadWalletUnlocker,
|
|
getHeaders,
|
|
getLock,
|
|
setLock,
|
|
getRoute,
|
|
keysendMessage,
|
|
signMessage,
|
|
verifyMessage,
|
|
signAscii,
|
|
signBuffer,
|
|
LND_KEYSEND_KEY,
|
|
SPHINX_CUSTOM_RECORD_KEY,
|
|
listInvoices,
|
|
listPayments,
|
|
checkConnection,
|
|
}
|
|
|