|
|
@ -18,31 +18,65 @@ const lightning = require("../utils/lightning"); |
|
|
|
const wallet_1 = require("../utils/wallet"); |
|
|
|
const jsonUtils = require("../utils/json"); |
|
|
|
const sequelize_1 = require("sequelize"); |
|
|
|
const node_fetch_1 = require("node-fetch"); |
|
|
|
const helpers = require("../helpers"); |
|
|
|
let queries = {}; |
|
|
|
const hub_pubkey = '023d70f2f76d283c6c4e58109ee3a2816eb9d8feb40b23d62469060a2b2867b77f'; |
|
|
|
let hub_pubkey = ''; |
|
|
|
const hub_url = 'https://hub.sphinx.chat/api/v1/'; |
|
|
|
function get_hub_pubkey() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const r = yield node_fetch_1.default(hub_url + '/routingnode'); |
|
|
|
const j = yield r.json(); |
|
|
|
if (j && j.pubkey) { |
|
|
|
console.log("=> GOT HUB PUBKEY", j.pubkey); |
|
|
|
hub_pubkey = j.pubkey; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
get_hub_pubkey(); |
|
|
|
function getReceivedAccountings() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const accountings = yield models_1.models.Accounting.findAll({ |
|
|
|
where: { |
|
|
|
status: constants_1.default.statuses.received |
|
|
|
} |
|
|
|
}); |
|
|
|
return accountings.map(a => (a.dataValues || a)); |
|
|
|
}); |
|
|
|
} |
|
|
|
function getPendingAccountings() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const utxos = yield wallet_1.listUnspent(); |
|
|
|
const accountings = yield models_1.models.Accounting.findAll({ |
|
|
|
where: { |
|
|
|
onchain_address: { |
|
|
|
[sequelize_1.Op.in]: utxos.map(utxo => utxo.address) |
|
|
|
}, |
|
|
|
status: constants_1.default.statuses.pending |
|
|
|
} |
|
|
|
}); |
|
|
|
const ret = []; |
|
|
|
accountings.forEach(a => { |
|
|
|
const utxo = utxos.find(u => u.address === a.onchainAddress); |
|
|
|
if (utxo) { |
|
|
|
ret.push({ |
|
|
|
id: a.id, |
|
|
|
pubkey: a.pubkey, |
|
|
|
onchainAddress: utxo.address, |
|
|
|
amount: utxo.amount_sat, |
|
|
|
confirmations: utxo.confirmations, |
|
|
|
sourceApp: a.sourceApp, |
|
|
|
date: a.date, |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
return ret; |
|
|
|
}); |
|
|
|
} |
|
|
|
function listUTXOs(req, res) { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
try { |
|
|
|
const utxos = yield wallet_1.listUnspent(); // at least 1 confg
|
|
|
|
const addys = utxos.map(utxo => utxo.address); |
|
|
|
const accountings = yield models_1.models.Accounting.findAll({ |
|
|
|
where: { |
|
|
|
onchain_address: { |
|
|
|
[sequelize_1.Op.in]: addys |
|
|
|
}, |
|
|
|
status: constants_1.default.statuses.pending |
|
|
|
} |
|
|
|
}); |
|
|
|
const ret = []; |
|
|
|
accountings.forEach(a => { |
|
|
|
const acc = Object.assign({}, a.dataValues); |
|
|
|
const utxo = utxos.find(u => u.address === a.onchainAddress); |
|
|
|
if (utxo) { |
|
|
|
acc.amount = utxo.amount_sat; |
|
|
|
acc.confirmations = utxo.confirmations; |
|
|
|
ret.push(acc); |
|
|
|
} |
|
|
|
}); |
|
|
|
const ret = yield getPendingAccountings(); |
|
|
|
res_1.success(res, ret.map(acc => jsonUtils.accountingToJson(acc))); |
|
|
|
} |
|
|
|
catch (e) { |
|
|
@ -51,9 +85,132 @@ function listUTXOs(req, res) { |
|
|
|
}); |
|
|
|
} |
|
|
|
exports.listUTXOs = listUTXOs; |
|
|
|
function getSuggestedSatPerByte() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const MAX_AMT = 250; |
|
|
|
try { |
|
|
|
const r = yield node_fetch_1.default('https://mempool.space/api/v1/fees/recommended'); |
|
|
|
const j = yield r.json(); |
|
|
|
return Math.min(MAX_AMT, j.halfHourFee); |
|
|
|
} |
|
|
|
catch (e) { |
|
|
|
return MAX_AMT; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
// https://mempool.space/api/v1/fees/recommended
|
|
|
|
function genChannelAndConfirmAccounting(acc) { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
console.log("[WATCH]=> genChannelAndConfirmAccounting"); |
|
|
|
const sat_per_byte = yield getSuggestedSatPerByte(); |
|
|
|
console.log("[WATCH]=> sat_per_byte", sat_per_byte); |
|
|
|
try { |
|
|
|
const r = yield lightning.openChannel({ |
|
|
|
node_pubkey: acc.pubkey, |
|
|
|
local_funding_amount: acc.amount, |
|
|
|
push_sat: 0, |
|
|
|
sat_per_byte |
|
|
|
}); |
|
|
|
console.log("[WATCH]=> CHANNEL OPENED!", r); |
|
|
|
const fundingTxidRev = Buffer.from(r.funding_txid_bytes).toString('hex'); |
|
|
|
const fundingTxid = fundingTxidRev.match(/.{2}/g).reverse().join(""); |
|
|
|
yield models_1.models.Accounting.update({ |
|
|
|
status: constants_1.default.statuses.received, |
|
|
|
fundingTxid: fundingTxid, |
|
|
|
amount: acc.amount |
|
|
|
}, { |
|
|
|
where: { id: acc.id } |
|
|
|
}); |
|
|
|
console.log("[WATCH]=> ACCOUNTINGS UPDATED to received!", acc.id); |
|
|
|
} |
|
|
|
catch (e) { |
|
|
|
console.log('[ACCOUNTING] error creating channel', e); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
function pollUTXOs() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
// console.log("[WATCH]=> pollUTXOs")
|
|
|
|
const accs = yield getPendingAccountings(); |
|
|
|
if (!accs) |
|
|
|
return; |
|
|
|
// console.log("[WATCH]=> accs", accs.length)
|
|
|
|
yield asyncForEach(accs, (acc) => __awaiter(this, void 0, void 0, function* () { |
|
|
|
if (acc.confirmations <= 0) |
|
|
|
return; // needs confs
|
|
|
|
if (acc.amount <= 0) |
|
|
|
return; // needs amount
|
|
|
|
if (!acc.pubkey) |
|
|
|
return; // this shouldnt happen
|
|
|
|
yield genChannelAndConfirmAccounting(acc); |
|
|
|
})); |
|
|
|
yield checkForConfirmedChannels(); |
|
|
|
}); |
|
|
|
} |
|
|
|
function checkForConfirmedChannels() { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const received = yield getReceivedAccountings(); |
|
|
|
// console.log('[WATCH] received accountings:', received)
|
|
|
|
yield asyncForEach(received, (rec) => __awaiter(this, void 0, void 0, function* () { |
|
|
|
if (rec.amount <= 0) |
|
|
|
return; // needs amount
|
|
|
|
if (!rec.pubkey) |
|
|
|
return; // this shouldnt happen
|
|
|
|
if (!rec.fundingTxid) |
|
|
|
return; |
|
|
|
yield checkChannelsAndKeysend(rec); |
|
|
|
})); |
|
|
|
}); |
|
|
|
} |
|
|
|
function checkChannelsAndKeysend(rec) { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } }); |
|
|
|
const chans = yield lightning.listChannels({ |
|
|
|
active_only: true, |
|
|
|
peer: rec.pubkey |
|
|
|
}); |
|
|
|
console.log('[WATCH] chans for pubkey:', rec.pubkey, chans); |
|
|
|
if (!(chans && chans.channels)) |
|
|
|
return; |
|
|
|
chans.channels.forEach(chan => { |
|
|
|
if (chan.channel_point.includes(rec.fundingTxid)) { |
|
|
|
console.log('[WATCH] found channel to keysend!', chan); |
|
|
|
const msg = { |
|
|
|
type: constants_1.default.message_types.keysend, |
|
|
|
}; |
|
|
|
const MINUS_AMT = 2000; |
|
|
|
const amount = rec.amount - parseInt(chan.local_chan_reserve_sat || 0) - parseInt(chan.remote_chan_reserve_sat || 0) - parseInt(chan.commit_fee || 0) - MINUS_AMT; |
|
|
|
console.log('[WATCH] amt to final keysend', amount); |
|
|
|
helpers.performKeysendMessage({ |
|
|
|
sender: owner, |
|
|
|
destination_key: rec.pubkey, |
|
|
|
amount, msg, |
|
|
|
success: function () { |
|
|
|
console.log('[WATCH] complete! Updating accounting, id:', rec.id); |
|
|
|
models_1.models.Accounting.update({ |
|
|
|
status: constants_1.default.statuses.confirmed, |
|
|
|
chanId: chan.chan_id |
|
|
|
}, { |
|
|
|
where: { id: rec.id } |
|
|
|
}); |
|
|
|
}, |
|
|
|
failure: function () { |
|
|
|
console.log('[WATCH] failed final keysend'); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
function startWatchingUTXOs() { |
|
|
|
setInterval(pollUTXOs, 600000); // every 10 minutes
|
|
|
|
} |
|
|
|
exports.startWatchingUTXOs = startWatchingUTXOs; |
|
|
|
function queryOnchainAddress(req, res) { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
console.log('=> queryOnchainAddress'); |
|
|
|
if (!hub_pubkey) |
|
|
|
return console.log("=> NO ROUTING NODE PUBKEY SET"); |
|
|
|
const uuid = short.generate(); |
|
|
|
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } }); |
|
|
|
const app = req.params.app; |
|
|
@ -170,4 +327,11 @@ exports.receiveQueryResponse = (payload) => __awaiter(void 0, void 0, void 0, fu |
|
|
|
console.log("=> ERROR receiveQueryResponse,", e); |
|
|
|
} |
|
|
|
}); |
|
|
|
function asyncForEach(array, callback) { |
|
|
|
return __awaiter(this, void 0, void 0, function* () { |
|
|
|
for (let index = 0; index < array.length; index++) { |
|
|
|
yield callback(array[index], index, array); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
//# sourceMappingURL=queries.js.map
|