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

Misc #431

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open

Misc #431

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
150 changes: 116 additions & 34 deletions class/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export class User {
return this._password;
}
getAccessToken() {
return this._acess_token;
return this._access_token;
}
getRefreshToken() {
return this._refresh_token;
Expand All @@ -48,37 +48,41 @@ export class User {
let access_token = authorization.replace('Bearer ', '');
let userid = await this._redis.get('userid_for_' + access_token);

if (userid) {
this._userid = userid;
return true;
if (!userid) {
return false
}

return false;
let refresh_token = await this._redis.get('refresh_token_for_' + userid)
if (refresh_token === access_token) {
return false
}

this._userid = userid;
return true;
}

async loadByRefreshToken(refresh_token) {
let userid = await this._redis.get('userid_for_' + refresh_token);
if (userid) {
this._userid = userid;
await this._generateTokens();
return true;

if (!userid) {
return false;
}

return false;
let access_token = await this._redis.get('access_token_for_' + userid)
if (access_token === refresh_token) {
return false
}

this._userid = userid;
await this._generateTokens();
return true;
}

async create() {
let buffer = crypto.randomBytes(10);
let login = buffer.toString('hex');

buffer = crypto.randomBytes(10);
let password = buffer.toString('hex');
this._login = this._generateDigest();
this._password = this._generateDigest();
this._userid = this._generateDigest();

buffer = crypto.randomBytes(24);
let userid = buffer.toString('hex');
this._login = login;
this._password = password;
this._userid = userid;
await this._saveUserToDatabase();
}

Expand Down Expand Up @@ -326,28 +330,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 @@ -374,17 +401,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 @@ -414,8 +449,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 @@ -449,12 +487,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 @@ -467,41 +508,80 @@ 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);
}
}
return result;
}

async _generateTokens() {
let buffer = crypto.randomBytes(20);
this._acess_token = buffer.toString('hex');
await this._invalidateCurrentTokens();

await this._generateAccessToken();
await this._generateRefreshToken();
}

async _invalidateCurrentTokens() {
this._access_token = await this._redis.get('access_token_for_' + this._userid);
this._refresh_token = await this._redis.get('refresh_token_for_' + this._userid);

buffer = crypto.randomBytes(20);
this._refresh_token = buffer.toString('hex');
await this._redis.del('access_token_for_' + this._userid);
await this._redis.del('refresh_token_for_' + this._userid);
await this._redis.del('userid_for_' + this._access_token);
await this._redis.del('userid_for_' + this._refresh_token);

await this._redis.set('userid_for_' + this._acess_token, this._userid);
await this._redis.set('userid_for_' + this._refresh_token, this._userid);
await this._redis.set('access_token_for_' + this._userid, this._acess_token);
await this._redis.set('refresh_token_for_' + this._userid, this._refresh_token);
this._access_token = null
this._refresh_token = null
}

async _generateAccessToken() {
this._access_token = this._generateDigest();

const key_UId_AT = 'userid_for_' + this._access_token;
const key_AT_UId = 'access_token_for_' + this._userid;

await this._redis.set(key_UId_AT, this._userid);
await this._redis.set(key_AT_UId, this._access_token);

await this._redis.expire(key_UId_AT, accessTokenLifeTime);
await this._redis.expire(key_AT_UId, accessTokenLifeTime);
}

async _generateRefreshToken() {
this._refresh_token = this._generateDigest();

const key_UId_RT = 'userid_for_' + this._refresh_token;
const key_RT_UId = 'refresh_token_for_' + this._userid;

await this._redis.set(key_UId_RT, this._userid);
await this._redis.set(key_RT_UId, this._refresh_token);

await this._redis.expire(key_UId_RT, refreshTokenLifeTime);
await this._redis.expire(key_RT_UId, refreshTokenLifeTime);
}

async _saveUserToDatabase() {
let key;
await this._redis.set((key = 'user_' + this._login + '_' + this._hash(this._password)), this._userid);
}

_generateDigest() {
const buffer = crypto.randomBytes(256);
return crypto.createHash('sha1').update(buffer).digest('hex');
}

/**
* Fetches all onchain txs for user's address, and compares them to
* already imported txids (stored in database); Ones that are not imported -
Expand Down Expand Up @@ -553,6 +633,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
7 changes: 7 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ let config = {
rateLimit: 200,
forwardReserveFee: 0.01, // default 0.01
intraHubFee: 0.003, // default 0.003
auth: {
accessTokenLifeTime: 3600,
refreshTokenLifeTime: 86400,
},
bitcoind: {
rpc: 'http://login:password@1.1.1.1:8332/wallet/wallet.dat',
},
Expand All @@ -18,6 +22,9 @@ let config = {
url: '1.1.1.1:10009',
password: '',
},
bitcoin: {
confirmations: 3,
},
};

if (process.env.CONFIG) {
Expand Down
Loading