Browse Source

Merge pull request #139 from stakwork/watch-addys

Watch addys
master v1.3.1
Evan Feenstra 4 years ago
committed by GitHub
parent
commit
f76cc8904a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      dist/src/controllers/index.js
  2. 2
      dist/src/controllers/index.js.map
  3. 1
      dist/src/controllers/messages.js
  4. 2
      dist/src/controllers/messages.js.map
  5. 184
      dist/src/controllers/queries.js
  6. 2
      dist/src/controllers/queries.js.map
  7. 4
      dist/src/models/ts/accounting.js
  8. 2
      dist/src/models/ts/accounting.js.map
  9. 28
      dist/src/utils/lightning.js
  10. 2
      dist/src/utils/lightning.js.map
  11. 1
      dist/src/utils/setup.js
  12. 2
      dist/src/utils/setup.js.map
  13. 2
      src/controllers/index.ts
  14. 1
      src/controllers/messages.ts
  15. 177
      src/controllers/queries.ts
  16. 3
      src/models/ts/accounting.ts
  17. 36
      src/utils/lightning.ts
  18. 2
      src/utils/setup.ts

1
dist/src/controllers/index.js

@ -38,6 +38,7 @@ function set(app) {
} }
media.cycleMediaToken(); media.cycleMediaToken();
timers.reloadTimers(); timers.reloadTimers();
queries.startWatchingUTXOs();
app.get('/chats', chats.getChats); app.get('/chats', chats.getChats);
app.post('/group', chats.createGroupChat); app.post('/group', chats.createGroupChat);
app.put('/chats/:id', chats.updateChat); app.put('/chats/:id', chats.updateChat);

2
dist/src/controllers/index.js.map

File diff suppressed because one or more lines are too long

1
dist/src/controllers/messages.js

@ -126,7 +126,6 @@ exports.getMsgs = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
clause.limit = limit; clause.limit = limit;
clause.offset = offset; clause.offset = offset;
} }
console.log('=> clause:', clause);
const messages = yield models_1.models.Message.findAll(clause); const messages = yield models_1.models.Message.findAll(clause);
console.log('=> got msgs', (messages && messages.length)); console.log('=> got msgs', (messages && messages.length));
const chatIds = []; const chatIds = [];

2
dist/src/controllers/messages.js.map

File diff suppressed because one or more lines are too long

184
dist/src/controllers/queries.js

@ -18,31 +18,65 @@ const lightning = require("../utils/lightning");
const wallet_1 = require("../utils/wallet"); const wallet_1 = require("../utils/wallet");
const jsonUtils = require("../utils/json"); const jsonUtils = require("../utils/json");
const sequelize_1 = require("sequelize"); const sequelize_1 = require("sequelize");
const node_fetch_1 = require("node-fetch");
const helpers = require("../helpers");
let queries = {}; let queries = {};
const hub_pubkey = '023d70f2f76d283c6c4e58109ee3a2816eb9d8feb40b23d62469060a2b2867b77f'; let hub_pubkey = '';
function listUTXOs(req, res) { const hub_url = 'https://hub.sphinx.chat/api/v1/';
function get_hub_pubkey() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { const r = yield node_fetch_1.default(hub_url + '/routingnode');
const utxos = yield wallet_1.listUnspent(); // at least 1 confg const j = yield r.json();
const addys = utxos.map(utxo => utxo.address); 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({ const accountings = yield models_1.models.Accounting.findAll({
where: { where: {
onchain_address: { onchain_address: {
[sequelize_1.Op.in]: addys [sequelize_1.Op.in]: utxos.map(utxo => utxo.address)
}, },
status: constants_1.default.statuses.pending status: constants_1.default.statuses.pending
} }
}); });
const ret = []; const ret = [];
accountings.forEach(a => { accountings.forEach(a => {
const acc = Object.assign({}, a.dataValues);
const utxo = utxos.find(u => u.address === a.onchainAddress); const utxo = utxos.find(u => u.address === a.onchainAddress);
if (utxo) { if (utxo) {
acc.amount = utxo.amount_sat; ret.push({
acc.confirmations = utxo.confirmations; id: a.id,
ret.push(acc); 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 ret = yield getPendingAccountings();
res_1.success(res, ret.map(acc => jsonUtils.accountingToJson(acc))); res_1.success(res, ret.map(acc => jsonUtils.accountingToJson(acc)));
} }
catch (e) { catch (e) {
@ -51,9 +85,132 @@ function listUTXOs(req, res) {
}); });
} }
exports.listUTXOs = listUTXOs; 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) { function queryOnchainAddress(req, res) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
console.log('=> queryOnchainAddress'); console.log('=> queryOnchainAddress');
if (!hub_pubkey)
return console.log("=> NO ROUTING NODE PUBKEY SET");
const uuid = short.generate(); const uuid = short.generate();
const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } }); const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
const app = req.params.app; 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); 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 //# sourceMappingURL=queries.js.map

2
dist/src/controllers/queries.js.map

File diff suppressed because one or more lines are too long

4
dist/src/models/ts/accounting.js

@ -53,6 +53,10 @@ __decorate([
sequelize_typescript_1.Column(sequelize_typescript_1.DataType.BIGINT), sequelize_typescript_1.Column(sequelize_typescript_1.DataType.BIGINT),
__metadata("design:type", Number) __metadata("design:type", Number)
], Accounting.prototype, "chanId", void 0); ], Accounting.prototype, "chanId", void 0);
__decorate([
sequelize_typescript_1.Column,
__metadata("design:type", String)
], Accounting.prototype, "fundingTxid", void 0);
Accounting = __decorate([ Accounting = __decorate([
sequelize_typescript_1.Table({ tableName: 'sphinx_accountings', underscored: true }) sequelize_typescript_1.Table({ tableName: 'sphinx_accountings', underscored: true })
], Accounting); ], Accounting);

2
dist/src/models/ts/accounting.js.map

@ -1 +1 @@
{"version":3,"file":"accounting.js","sourceRoot":"","sources":["../../../../src/models/ts/accounting.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,UAAU,GAA/B,MAAqB,UAAW,SAAQ,4BAAiB;CAkCxD,CAAA;AA1BC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;sCACQ;AAGV;IADC,6BAAM;8BACD,IAAI;wCAAA;AAGV;IADC,6BAAM;;0CACO;AAGd;IADC,6BAAM;;kDACe;AAGtB;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;0CACX;AAGd;IADC,6BAAM;;6CACU;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;0CACV;AAGd;IADC,6BAAM;;yCACM;AAGb;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;0CACV;AAhCK,UAAU;IAD9B,4BAAK,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;GACzC,UAAU,CAkC9B;kBAlCoB,UAAU"} {"version":3,"file":"accounting.js","sourceRoot":"","sources":["../../../../src/models/ts/accounting.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,+DAAsE;AAGtE,IAAqB,UAAU,GAA/B,MAAqB,UAAW,SAAQ,4BAAiB;CAqCxD,CAAA;AA7BC;IANC,6BAAM,CAAC;QACN,IAAI,EAAE,+BAAQ,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,MAAM,EAAE,IAAI;QACZ,aAAa,EAAE,IAAI;KACpB,CAAC;;sCACQ;AAGV;IADC,6BAAM;8BACD,IAAI;wCAAA;AAGV;IADC,6BAAM;;0CACO;AAGd;IADC,6BAAM;;kDACe;AAGtB;IADC,6BAAM,CAAC,+BAAQ,CAAC,OAAO,CAAC;;0CACX;AAGd;IADC,6BAAM;;6CACU;AAGjB;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;0CACV;AAGd;IADC,6BAAM;;yCACM;AAGb;IADC,6BAAM,CAAC,+BAAQ,CAAC,MAAM,CAAC;;0CACV;AAGd;IADC,6BAAM;;+CACY;AAnCA,UAAU;IAD9B,4BAAK,CAAC,EAAE,SAAS,EAAE,oBAAoB,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;GACzC,UAAU,CAqC9B;kBArCoB,UAAU"}

28
dist/src/utils/lightning.js

@ -487,11 +487,15 @@ function getInfo() {
}); });
} }
exports.getInfo = getInfo; exports.getInfo = getInfo;
function listChannels() { function listChannels(args) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const opts = args || {};
if (args && args.peer) {
opts.peer = ByteBuffer.fromHex(args.peer);
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const lightning = loadLightning(); const lightning = loadLightning();
lightning.listChannels({}, function (err, response) { lightning.listChannels(opts, function (err, response) {
if (err == null) { if (err == null) {
resolve(response); resolve(response);
} }
@ -503,6 +507,26 @@ function listChannels() {
}); });
} }
exports.listChannels = listChannels; exports.listChannels = listChannels;
function openChannel(args) {
return __awaiter(this, void 0, void 0, function* () {
const opts = args || {};
if (args && args.node_pubkey) {
opts.node_pubkey = ByteBuffer.fromHex(args.node_pubkey);
}
return new Promise((resolve, reject) => {
const lightning = loadLightning();
lightning.openChannelSync(opts, function (err, response) {
if (err == null) {
resolve(response);
}
else {
reject(err);
}
});
});
});
}
exports.openChannel = openChannel;
function channelBalance() { function channelBalance() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

2
dist/src/utils/lightning.js.map

File diff suppressed because one or more lines are too long

1
dist/src/utils/setup.js

@ -46,6 +46,7 @@ function setVersion() {
} }
function migrate() { function migrate() {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
addTableColumn('sphinx_accountings', 'funding_txid');
addTableColumn('sphinx_chat_members', 'last_alias'); addTableColumn('sphinx_chat_members', 'last_alias');
addTableColumn('sphinx_chats', 'my_photo_url'); addTableColumn('sphinx_chats', 'my_photo_url');
addTableColumn('sphinx_chats', 'my_alias'); addTableColumn('sphinx_chats', 'my_alias');

2
dist/src/utils/setup.js.map

File diff suppressed because one or more lines are too long

2
src/controllers/index.ts

@ -32,6 +32,8 @@ export async function set(app) {
timers.reloadTimers() timers.reloadTimers()
queries.startWatchingUTXOs()
app.get('/chats', chats.getChats) app.get('/chats', chats.getChats)
app.post('/group', chats.createGroupChat) app.post('/group', chats.createGroupChat)
app.put('/chats/:id', chats.updateChat) app.put('/chats/:id', chats.updateChat)

1
src/controllers/messages.ts

@ -136,7 +136,6 @@ export const getMsgs = async (req, res) => {
clause.limit = limit clause.limit = limit
clause.offset = offset clause.offset = offset
} }
console.log('=> clause:',clause)
const messages = await models.Message.findAll(clause) const messages = await models.Message.findAll(clause)
console.log('=> got msgs', (messages && messages.length)) console.log('=> got msgs', (messages && messages.length))
const chatIds: number[] = [] const chatIds: number[] = []

177
src/controllers/queries.ts

@ -7,6 +7,8 @@ import * as lightning from '../utils/lightning'
import { listUnspent, UTXO } from '../utils/wallet' import { listUnspent, UTXO } from '../utils/wallet'
import * as jsonUtils from '../utils/json' import * as jsonUtils from '../utils/json'
import { Op } from 'sequelize' import { Op } from 'sequelize'
import fetch from 'node-fetch'
import * as helpers from '../helpers'
type QueryType = 'onchain_address' type QueryType = 'onchain_address'
export interface Query { export interface Query {
@ -18,41 +20,188 @@ export interface Query {
let queries: { [k: string]: Query } = {} let queries: { [k: string]: Query } = {}
const hub_pubkey = '023d70f2f76d283c6c4e58109ee3a2816eb9d8feb40b23d62469060a2b2867b77f' let hub_pubkey = ''
export async function listUTXOs(req, res) { const hub_url = 'https://hub.sphinx.chat/api/v1/'
try { async function get_hub_pubkey(){
const utxos: UTXO[] = await listUnspent() // at least 1 confg const r = await fetch(hub_url+'/routingnode')
const addys = utxos.map(utxo => utxo.address) const j = await r.json()
if(j && j.pubkey) {
console.log("=> GOT HUB PUBKEY", j.pubkey)
hub_pubkey = j.pubkey
}
}
get_hub_pubkey()
interface Accounting {
id: number
pubkey: string
onchainAddress: string
amount: number
confirmations: number
sourceApp: string
date: string
fundingTxid: string
}
async function getReceivedAccountings(): Promise<Accounting[]> {
const accountings = await models.Accounting.findAll({
where: {
status: constants.statuses.received
}
})
return accountings.map(a => (a.dataValues || a))
}
async function getPendingAccountings(): Promise<Accounting[]> {
const utxos: UTXO[] = await listUnspent()
const accountings = await models.Accounting.findAll({ const accountings = await models.Accounting.findAll({
where: { where: {
onchain_address: { onchain_address: {
[Op.in]: addys [Op.in]: utxos.map(utxo => utxo.address)
}, },
status: constants.statuses.pending status: constants.statuses.pending
} }
}) })
const ret: any[] = [] const ret: Accounting[] = []
accountings.forEach(a => { accountings.forEach(a => {
const acc = { ...a.dataValues }
const utxo = utxos.find(u => u.address === a.onchainAddress) const utxo = utxos.find(u => u.address === a.onchainAddress)
if (utxo) { if (utxo) {
acc.amount = utxo.amount_sat ret.push(<Accounting>{
acc.confirmations = utxo.confirmations id: a.id,
ret.push(acc) pubkey: a.pubkey,
onchainAddress: utxo.address,
amount: utxo.amount_sat,
confirmations: utxo.confirmations,
sourceApp: a.sourceApp,
date: a.date,
})
} }
}) })
return ret
}
export async function listUTXOs(req, res) {
try {
const ret: Accounting[] = await getPendingAccountings()
success(res, ret.map(acc => jsonUtils.accountingToJson(acc))) success(res, ret.map(acc => jsonUtils.accountingToJson(acc)))
} catch (e) { } catch (e) {
failure(res, e) failure(res, e)
} }
} }
async function getSuggestedSatPerByte(): Promise<number> {
const MAX_AMT = 250
try {
const r = await fetch('https://mempool.space/api/v1/fees/recommended')
const j = await r.json()
return Math.min(MAX_AMT, j.halfHourFee)
} catch (e) {
return MAX_AMT
}
}
// https://mempool.space/api/v1/fees/recommended
async function genChannelAndConfirmAccounting(acc: Accounting) {
console.log("[WATCH]=> genChannelAndConfirmAccounting")
const sat_per_byte = await getSuggestedSatPerByte()
console.log("[WATCH]=> sat_per_byte", sat_per_byte)
try {
const r = await 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) as any).reverse().join("")
await models.Accounting.update({
status: constants.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)
}
}
async function pollUTXOs() {
// console.log("[WATCH]=> pollUTXOs")
const accs: Accounting[] = await getPendingAccountings()
if (!accs) return
// console.log("[WATCH]=> accs", accs.length)
await asyncForEach(accs, async (acc: Accounting) => {
if (acc.confirmations <= 0) return // needs confs
if (acc.amount <= 0) return // needs amount
if (!acc.pubkey) return // this shouldnt happen
await genChannelAndConfirmAccounting(acc)
})
await checkForConfirmedChannels()
}
async function checkForConfirmedChannels(){
const received = await getReceivedAccountings()
// console.log('[WATCH] received accountings:', received)
await asyncForEach(received, async (rec: Accounting) => {
if (rec.amount <= 0) return // needs amount
if (!rec.pubkey) return // this shouldnt happen
if (!rec.fundingTxid) return
await checkChannelsAndKeysend(rec)
})
}
async function checkChannelsAndKeysend(rec: Accounting){
const owner = await models.Contact.findOne({ where: { isOwner: true } })
const chans = await 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=>{ // find by txid
if(chan.channel_point.includes(rec.fundingTxid)) {
console.log('[WATCH] found channel to keysend!', chan)
const msg: { [k: string]: any } = {
type: constants.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.Accounting.update({
status: constants.statuses.confirmed,
chanId: chan.chan_id
}, {
where: { id: rec.id }
})
},
failure: function(){
console.log('[WATCH] failed final keysend')
}
})
}
})
}
export function startWatchingUTXOs() {
setInterval(pollUTXOs, 600000) // every 10 minutes
}
export async function queryOnchainAddress(req, res) { export async function queryOnchainAddress(req, res) {
console.log('=> queryOnchainAddress') console.log('=> queryOnchainAddress')
if(!hub_pubkey) return console.log("=> NO ROUTING NODE PUBKEY SET")
const uuid = short.generate() const uuid = short.generate()
const owner = await models.Contact.findOne({ where: { isOwner: true } }) const owner = await models.Contact.findOne({ where: { isOwner: true } })
const app = req.params.app; const app = req.params.app;
@ -169,3 +318,9 @@ export const receiveQueryResponse = async (payload) => {
console.log("=> ERROR receiveQueryResponse,", e) console.log("=> ERROR receiveQueryResponse,", e)
} }
} }
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}

3
src/models/ts/accounting.ts

@ -35,4 +35,7 @@ export default class Accounting extends Model<Accounting> {
@Column(DataType.BIGINT) @Column(DataType.BIGINT)
chanId: number chanId: number
@Column
fundingTxid: string
} }

36
src/utils/lightning.ts

@ -436,10 +436,42 @@ async function getInfo(): Promise<{ [k: string]: any }> {
}) })
} }
async function listChannels(): Promise<{ [k: string]: any }> { interface ListChannelsArgs {
active_only?: boolean
inactive_only?: boolean
peer?: string // HEX!
}
async function listChannels(args?:ListChannelsArgs): Promise<{ [k: string]: any }> {
const opts:{[k:string]:any} = args || {}
if(args && args.peer) {
opts.peer = ByteBuffer.fromHex(args.peer)
}
return new Promise((resolve, reject) => {
const lightning = loadLightning()
lightning.listChannels(opts, function (err, response) {
if (err == null) {
resolve(response)
} else {
reject(err)
}
});
})
}
export interface OpenChannelArgs {
node_pubkey: any // bytes
local_funding_amount: number
push_sat: number // 0
sat_per_byte: number // 75?
}
export async function openChannel(args: OpenChannelArgs): Promise<{ [k: string]: any }> {
const opts = args||{}
if(args && args.node_pubkey) {
opts.node_pubkey = ByteBuffer.fromHex(args.node_pubkey)
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const lightning = loadLightning() const lightning = loadLightning()
lightning.listChannels({}, function (err, response) { lightning.openChannelSync(opts, function (err, response) {
if (err == null) { if (err == null) {
resolve(response) resolve(response)
} else { } else {

2
src/utils/setup.ts

@ -34,6 +34,8 @@ async function setVersion() {
async function migrate() { async function migrate() {
addTableColumn('sphinx_accountings', 'funding_txid')
addTableColumn('sphinx_chat_members', 'last_alias') addTableColumn('sphinx_chat_members', 'last_alias')
addTableColumn('sphinx_chats', 'my_photo_url') addTableColumn('sphinx_chats', 'my_photo_url')

Loading…
Cancel
Save