Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extended api #334

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions class/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,28 +320,51 @@ export class User {
return result;
}

async getUserInvoiceByHash(hash) {
const invoices = await this.getUserInvoices();
return invoices.find(i => Buffer.from(i.r_hash).toString('hex') === hash);
}

async addAddress(address) {
await this._redis.set('bitcoin_address_for_' + this._userid, address);
}

/**
* User's onchain txs that are >= 3 confs
* User's onchain txs that are >= configured target confirmations
* Queries bitcoind RPC.
*
* @returns {Promise<Array>}
*/
async getTxs() {
let onchainTx = await this.getOnchainTxs()
let lightningTxs = await this.getLightningTxs()

return [...onchainTx, ...lightningTxs]
}

async getOnchainTxs() {
const addr = await this.getOrGenerateAddress();
const targetConfirmations = config.bitcoin.confirmations;
let txs = await this._listtransactions();
txs = txs.result;
let result = [];
for (let tx of txs) {
if (tx.confirmations >= 3 && tx.address === addr && tx.category === 'receive') {
if (tx.confirmations >= targetConfirmations && tx.address === addr && tx.category === 'receive') {
tx.type = 'bitcoind_tx';
result.push(tx);
}
}

return result
}

async getOnchainTxById(id) {
const txs = await this.getOnchainTxs();
return txs.find(tx => id === `${tx.txid}${tx.vout}`);
}

async getLightningTxs() {
const result = []
let range = await this._redis.lrange('txs_for_' + this._userid, 0, -1);
for (let invoice of range) {
invoice = JSON.parse(invoice);
Expand All @@ -368,17 +391,25 @@ export class User {
if (invoice.payment_preimage) {
invoice.payment_preimage = Buffer.from(invoice.payment_preimage, 'hex').toString('hex');
}
let hash = lightningPayReq.decode(invoice.pay_req).tags.find(t => t.tagName === 'payment_hash')
invoice.r_hash = Buffer.from(hash.data, 'hex')
// removing unsued by client fields to reduce size
delete invoice.payment_error;
delete invoice.payment_route;
delete invoice.pay_req;
delete invoice.decoded;

result.push(invoice);
}

return result;
}

async getLightningTxByHash(hash) {
const txs = await this.getLightningTxs()
return txs.find(tx => Buffer.from(tx.r_hash).toString('hex') === hash);
}

/**
* Simple caching for this._bitcoindrpc.request('listtransactions', ['*', 100500, 0, true]);
* since its too much to fetch from bitcoind every time
Expand Down Expand Up @@ -408,8 +439,11 @@ export class User {
// now, compacting response a bit
for (const tx of txs.result) {
ret.result.push({
txid: tx.txid,
vout: tx.vout,
category: tx.category,
amount: tx.amount,
fee: tx.fee,
confirmations: tx.confirmations,
address: tx.address,
time: tx.blocktime || tx.time,
Expand Down Expand Up @@ -443,12 +477,15 @@ export class User {
.filter((tx) => tx.label !== 'external' && !tx.label.includes('openchannel'))
.map((tx) => {
const decodedTx = decodeRawHex(tx.raw_tx_hex);
decodedTx.outputs.forEach((vout) =>
decodedTx.outputs.forEach((vout, i) =>
outTxns.push({
// mark all as received, since external is filtered out
txid: tx.hash,
vout: i,
category: 'receive',
confirmations: tx.num_confirmations,
amount: Number(vout.value),
fee: Math.ceil(decodedTx.fees / decodedTx.vout_sz),
confirmations: tx.num_confirmations,
address: vout.scriptPubKey.addresses[0],
time: tx.time_stamp,
}),
Expand All @@ -461,17 +498,18 @@ export class User {
}

/**
* Returning onchain txs for user's address that are less than 3 confs
* Returning onchain txs for user's address that are less than configured target confirmations
*
* @returns {Promise<Array>}
*/
async getPendingTxs() {
const addr = await this.getOrGenerateAddress();
const targetConfirmations = config.bitcoin.confirmations;
let txs = await this._listtransactions();
txs = txs.result;
let result = [];
for (let tx of txs) {
if (tx.confirmations < 3 && tx.address === addr && tx.category === 'receive') {
if (tx.confirmations < targetConfirmations && tx.address === addr && tx.category === 'receive') {
result.push(tx);
}
}
Expand Down Expand Up @@ -547,6 +585,8 @@ export class User {
amount: +decodedInvoice.num_satoshis,
timestamp: Math.floor(+new Date() / 1000),
};
const hash = lightningPayReq.decode(pay_req).tags.find(t => t.tagName === 'payment_hash')
doc.r_hash = Buffer.from(hash.data, 'hex')

return this._redis.rpush('locked_payments_for_' + this._userid, JSON.stringify(doc));
}
Expand Down
3 changes: 3 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ let config = {
url: '1.1.1.1:10009',
password: '',
},
bitcoin: {
confirmations: 3,
},
};

if (process.env.CONFIG) {
Expand Down
65 changes: 65 additions & 0 deletions controllers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,50 @@ router.get('/getinfo', postLimiter, async function (req, res) {
});
});

router.get('/gettxs/:tx_hash', async function (req, res) {
const txHash = req.params.tx_hash;

logger.log('/gettxs/' + txHash, [req.id]);
let u = new User(redis, bitcoinclient, lightning);
if (!(await u.loadByAuthorization(req.headers.authorization))) {
return errorBadAuth(res);
}
logger.log('/gettxs/' + txHash, [req.id, 'userid: ' + u.getUserId()]);

if (!(await u.getAddress())) await u.generateAddress(); // onchain addr needed further
try {
await u.accountForPosibleTxids();

let tx = await u.getLightningTxByHash(txHash)
if (tx) {
return res.send(tx)
}

let lockedPayments = await u.getLockedPayments();
tx = lockedPayments.find(tx => Buffer.from(tx.r_hash).toString('hex') === hash)
if (tx) {
return res.send({
type: 'paid_invoice',
fee: Math.floor(tx.amount * forwardFee) /* feelimit */,
value: tx.amount + Math.floor(tx.amount * forwardFee) /* feelimit */,
timestamp: tx.timestamp,
memo: 'Payment in transition',
r_hash: tx.r_hash
})
}

tx = await u.getOnchainTxById(txHash);
if (tx) {
return res.send(tx)
}

res.send({});
} catch (Err) {
logger.log('', [req.id, 'error gettxs:', Err.message, 'userid:', u.getUserId()]);
res.send({});
}
});

router.get('/gettxs', async function (req, res) {
logger.log('/gettxs', [req.id]);
let u = new User(redis, bitcoinclient, lightning);
Expand All @@ -435,6 +479,7 @@ router.get('/gettxs', async function (req, res) {
value: locked.amount + Math.floor(locked.amount * forwardFee) /* feelimit */,
timestamp: locked.timestamp,
memo: 'Payment in transition',
r_hash: locked.r_hash,
});
}
res.send(txs);
Expand All @@ -444,6 +489,26 @@ router.get('/gettxs', async function (req, res) {
}
});

router.get('/getuserinvoices/:invoice_hash', postLimiter, async function (req, res) {
const invoiceHash = req.params.invoice_hash;
logger.log('/getuserinvoices/' + invoiceHash, [req.id]);

let u = new User(redis, bitcoinclient, lightning);
if (!(await u.loadByAuthorization(req.headers.authorization))) {
return errorBadAuth(res);
}

logger.log('/getuserinvoices'/ + invoiceHash, [req.id, 'userid: ' + u.getUserId()]);

try {
const invoice = await u.getUserInvoiceByHash(invoiceHash)
res.send(invoice || {})
} catch (Err) {
logger.log('', [req.id, 'error getting user invoice ' + invoiceHash + ':', Err.message, 'userid:', u.getUserId()]);
res.send({});
}
});

router.get('/getuserinvoices', postLimiter, async function (req, res) {
logger.log('/getuserinvoices', [req.id]);
let u = new User(redis, bitcoinclient, lightning);
Expand Down