aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2020-03-11 15:10:40 -0700
committerAdam Soltys <[email protected]>2020-03-11 15:10:40 -0700
commitc0888fab024cc9f7c99812bfabf9d7774312dbf7 (patch)
treecad95683af97432f6092a2c2bae10fbb7a588db5
parentbf2b51f0c2ae9b8f0f330dde6411e45fc0da8a25 (diff)
downloadcoinos-server-c0888fab024cc9f7c99812bfabf9d7774312dbf7.tar.gz
coinos-server-c0888fab024cc9f7c99812bfabf9d7774312dbf7.zip
refactor
-rw-r--r--.gitignore1
-rw-r--r--bitcoinPayments.js148
-rw-r--r--config/rpc.proto (renamed from rpc.proto)0
-rw-r--r--db/index.js (renamed from db.js)9
-rw-r--r--db/models/payments.js138
-rw-r--r--db/models/users.js246
-rw-r--r--db/schema.sql (renamed from schema.sql)0
-rw-r--r--index.js79
-rw-r--r--lib/binance.js (renamed from binance.js)5
-rw-r--r--lib/devrates.js (renamed from devrates.js)0
-rw-r--r--lib/passport.js36
-rw-r--r--lib/rates.js38
-rw-r--r--lib/sockets.js67
-rw-r--r--lib/whitelist.js (renamed from whitelist.js)2
-rw-r--r--liquidPayments.js145
-rw-r--r--lnd.js26
-rw-r--r--migrations/20180128181238-create-user.js60
-rw-r--r--migrations/20180206033508-create-transaction.js39
-rw-r--r--migrations/20180214033702-addUserBalance.js11
-rw-r--r--migrations/20180215052148-addChannelPoint.js11
-rw-r--r--migrations/20180217065619-addChannelBalance.js11
-rw-r--r--migrations/20180221010609-createInvoices.js32
-rw-r--r--migrations/20180312160019-addInvoiceRateAndCurrency.js13
-rw-r--r--migrations/20180315232457-renameInvoices.js15
-rw-r--r--migrations/20180316032817-addPaymentsAmount.js11
-rw-r--r--migrations/20180322163730-addTipToPayments.js11
-rw-r--r--migrations/20181008052713-addUserChannels.js11
-rw-r--r--migrations/20181008053428-addUsersBalances.js11
-rw-r--r--migrations/20190113193839-create_orders.js25
-rw-r--r--models/payments.js141
-rw-r--r--models/users.js249
-rw-r--r--passport.js39
-rw-r--r--payments.js41
-rw-r--r--queryRoutes.js13
-rw-r--r--rates.js42
-rw-r--r--routes/bitcoin/fee.js (renamed from estimateBitcoinFee.js)9
-rw-r--r--routes/bitcoin/receive.js143
-rw-r--r--routes/bitcoin/send.js (renamed from sendCoins.js)10
-rw-r--r--routes/lightning/invoice.js (renamed from addInvoice.js)2
-rw-r--r--routes/lightning/query.js7
-rw-r--r--routes/lightning/receive.js (renamed from lightningPayments.js)4
-rw-r--r--routes/lightning/send.js93
-rw-r--r--routes/lightning/user.js (renamed from payUser.js)2
-rw-r--r--routes/liquid/fee.js (renamed from estimateLiquidFee.js)16
-rw-r--r--routes/liquid/receive.js137
-rw-r--r--routes/liquid/send.js (renamed from sendLiquid.js)12
-rw-r--r--routes/payments.js40
-rw-r--r--routes/users.js328
-rw-r--r--sendPayment.js95
-rw-r--r--sockets.js74
-rw-r--r--users.js476
51 files changed, 1321 insertions, 1853 deletions
diff --git a/.gitignore b/.gitignore
index d4398339..411939f1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,6 @@ npm-debug.log
test/unit/coverage
test/e2e/reports
selenium-debug.log
-db
config.js
.env
config/config.json
diff --git a/bitcoinPayments.js b/bitcoinPayments.js
deleted file mode 100644
index 0d410a6a..00000000
--- a/bitcoinPayments.js
+++ /dev/null
@@ -1,148 +0,0 @@
-const reverse = require("buffer-reverse");
-const zmq = require("zeromq");
-const config = require("./config");
-const Sequelize = require("sequelize");
-
-const bitcoin = require("bitcoinjs-lib");
-const l = require("pino")();
-const toSats = n => parseInt((n * 100000000).toFixed())
-
-const zmqRawBlock = zmq.socket("sub");
-zmqRawBlock.connect(config.bitcoin.zmqrawblock);
-zmqRawBlock.subscribe("rawblock");
-
-const zmqRawTx = zmq.socket("sub");
-zmqRawTx.connect(config.bitcoin.zmqrawtx);
-zmqRawTx.subscribe("rawtx");
-
-let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
-
-module.exports = (app, bc, db, addresses, payments, emit) => {
- zmqRawTx.on("message", async (topic, message, sequence) => {
- const hex = message.toString("hex");
- let tx = bitcoin.Transaction.fromHex(message);
- let hash = reverse(tx.getHash()).toString("hex");
-
- if (payments.includes(hash)) return;
-
- Promise.all(
- tx.outs.map(async o => {
- const { value } = o;
-
- let address;
- try {
- address = bitcoin.address.fromOutputScript(
- o.script,
- NETWORK
- );
- } catch (e) {
- return;
- }
-
- if (Object.keys(addresses).includes(address)) {
- payments.push(hash);
-
- let user = await db.User.findOne({
- where: {
- username: addresses[address]
- }
- });
-
- let invoices = await db.Payment.findAll({
- limit: 1,
- where: {
- address,
- received: null,
- amount: {
- [Sequelize.Op.gt]: 0
- }
- },
- order: [["createdAt", "DESC"]]
- });
-
- let tip = null;
- if (invoices.length) tip = invoices[0].tip;
-
- let confirmed = false;
-
- user.address = await bc.getNewAddress("", "bech32");
- user.pending += value;
-
- await user.save();
- emit(user.username, "user", user);
-
- addresses[user.address] = user.username;
-
- const payment = await db.Payment.create({
- user_id: user.id,
- hash,
- amount: value,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- received: true,
- tip,
- confirmed,
- address,
- asset: 'BTC',
- });
-
- l.info("bitcoin detected", user.username, o.value);
- emit(user.username, "payment", payment);
- }
- })
- );
- });
-
- let queue = {};
-
- zmqRawBlock.on("message", async (topic, message, sequence) => {
- const payments = await db.Payment.findAll({
- where: { confirmed: false }
- });
-
- const hashes = payments.map(p => p.hash);
-
- let block = bitcoin.Block.fromHex(message.toString("hex"));
- block.transactions.map(tx => {
- let hash = reverse(tx.getHash()).toString("hex");
- if (hashes.includes(hash)) queue[hash] = 1;
- });
- });
-
- setInterval(async () => {
- let arr = Object.keys(queue);
- for (let i = 0; i < arr.length; i++) {
- let hash = arr[i];
-
- let p = await db.Payment.findOne({
- include: [{ model: db.User, as: "user" }],
- where: { hash, confirmed: 0 }
- })
-
- p.confirmed = 1;
-
- let user = await p.getUser();
- user.balance += p.amount;
- user.pending -= Math.min(user.pending, p.amount);
- emit(user.username, "user", user);
-
- await user.save();
- await p.save();
-
- let payments = await db.Payment.findAll({
- where: {
- user_id: user.id,
- received: {
- [Sequelize.Op.ne]: null
- },
- },
- order: [['id', 'DESC']],
- limit: 12
- });
-
- l.info("bitcoin confirmed", user.username, p.amount);
- emit(user.username, "payments", payments);
- delete queue[hash];
- }
- }, 1000);
-};
diff --git a/rpc.proto b/config/rpc.proto
index 76a78450..76a78450 100644
--- a/rpc.proto
+++ b/config/rpc.proto
diff --git a/db.js b/db/index.js
index a76e2957..dac6924e 100644
--- a/db.js
+++ b/db/index.js
@@ -1,16 +1,15 @@
-const config = require("./config");
const Sequelize = require("sequelize");
const conf = config.dbOptions;
-const db = new Sequelize(conf.database, conf.username, conf.password, {
+db = new Sequelize(conf.database, conf.username, conf.password, {
host: conf.host,
dialect: conf.dialect,
logging: false,
dialectOptions: { multipleStatements: true }
});
-db["User"] = require("./models/users.js")(db);
-db["Payment"] = require("./models/payments.js")(db);
+require("./models/users.js");
+require("./models/payments.js");
db["User"].hasMany(db["Payment"], {
as: "payments",
@@ -21,5 +20,3 @@ db["Payment"].belongsTo(db["User"], {
as: "user",
foreignKey: "user_id"
});
-
-module.exports = db;
diff --git a/db/models/payments.js b/db/models/payments.js
new file mode 100644
index 00000000..c6dd6e83
--- /dev/null
+++ b/db/models/payments.js
@@ -0,0 +1,138 @@
+const { DataTypes } = require('sequelize');
+
+const attributes = {
+ id: {
+ type: DataTypes.INTEGER(11),
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: true,
+ autoIncrement: true,
+ comment: null,
+ field: "id"
+ },
+ user_id: {
+ type: DataTypes.INTEGER(11),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "user_id"
+ },
+ hash: {
+ type: DataTypes.TEXT,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "hash"
+ },
+ createdAt: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "createdAt"
+ },
+ updatedAt: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "updatedAt"
+ },
+ rate: {
+ type: DataTypes.DOUBLE,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "rate"
+ },
+ asset: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "asset"
+ },
+ currency: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "currency"
+ },
+ address: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "address"
+ },
+ received: {
+ type: DataTypes.INTEGER(1),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "received"
+ },
+ amount: {
+ type: DataTypes.DOUBLE,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "amount"
+ },
+ fee: {
+ type: DataTypes.DOUBLE,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "fee"
+ },
+ tip: {
+ type: DataTypes.DOUBLE,
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "tip"
+ },
+ confirmed: {
+ type: DataTypes.INTEGER(1),
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "confirmed"
+ }
+};
+
+const options = {
+ tableName: "payments",
+ comment: "",
+ indexes: []
+};
+
+db["Payment"] = db.define("payments_model", attributes, options);
diff --git a/db/models/users.js b/db/models/users.js
new file mode 100644
index 00000000..d5b1f5b8
--- /dev/null
+++ b/db/models/users.js
@@ -0,0 +1,246 @@
+const { DataTypes } = require('sequelize');
+
+const attributes = {
+ id: {
+ type: DataTypes.INTEGER(11),
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: true,
+ autoIncrement: true,
+ comment: null,
+ field: "id"
+ },
+ username: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "username"
+ },
+ password: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "password"
+ },
+ liquid: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "liquid"
+ },
+ confidential: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "confidential"
+ },
+ address: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "address"
+ },
+ symbol: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "symbol"
+ },
+ otpsecret: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: "CAD",
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "otpsecret"
+ },
+ currency: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: "CAD",
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "currency"
+ },
+ currencies: {
+ type: DataTypes.JSON,
+ allowNull: false,
+ defaultValue: ["USD"],
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "currencies"
+ },
+ pubkey: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "pubkey"
+ },
+ privkey: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "privkey"
+ },
+ createdAt: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "createdAt"
+ },
+ updatedAt: {
+ type: DataTypes.DATE,
+ allowNull: false,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "updatedAt"
+ },
+ index: {
+ type: DataTypes.INTEGER(11),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "index"
+ },
+ balance: {
+ type: DataTypes.INTEGER(11),
+ allowNull: false,
+ defaultValue: "0",
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "balance"
+ },
+ channel: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "channel"
+ },
+ readonlypw: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "readonlypw"
+ },
+ limit: {
+ type: DataTypes.INTEGER(11),
+ allowNull: true,
+ defaultValue: "0",
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "limit"
+ },
+ fbtoken: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "fbtoken"
+ },
+ friend: {
+ type: DataTypes.INTEGER(1),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "friend"
+ },
+ pending: {
+ type: DataTypes.INTEGER(11),
+ allowNull: true,
+ defaultValue: "0",
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "pending"
+ },
+ name: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "name"
+ },
+ pic: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "pic"
+ },
+ twofa: {
+ type: DataTypes.INTEGER(1),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "twofa"
+ },
+ pin: {
+ type: DataTypes.STRING(255),
+ allowNull: true,
+ defaultValue: null,
+ primaryKey: false,
+ autoIncrement: false,
+ comment: null,
+ field: "pin"
+ },
+};
+
+const options = {
+ tableName: "users",
+ comment: "",
+ indexes: []
+};
+
+db["User"] = db.define("users_model", attributes, options);
diff --git a/schema.sql b/db/schema.sql
index 7bdf7d3b..7bdf7d3b 100644
--- a/schema.sql
+++ b/db/schema.sql
diff --git a/index.js b/index.js
index e2116a39..b84aa10f 100644
--- a/index.js
+++ b/index.js
@@ -1,63 +1,40 @@
const bodyParser = require("body-parser");
const compression = require("compression");
const cookieParser = require("cookie-parser");
-const BitcoinCore = require("bitcoin-core");
const cors = require("cors");
const express = require("express");
-const config = require("./config");
-const bolt11 = require("bolt11");
+l = require("pino")();
+config = require("./config");
-const l = require("pino")();
+SATS = 100000000;
+toSats = n => parseInt((n * SATS).toFixed())
-(async () => {
- const bc = new BitcoinCore(config.bitcoin);
- const app = express();
- app.enable("trust proxy");
- app.use(bodyParser.urlencoded({ extended: true }));
- app.use(bodyParser.json());
- app.use(cookieParser());
- app.use(cors({ credentials: true, origin: "http://*:*" }));
- app.use(compression());
+app = express();
+app.enable("trust proxy");
+app.use(bodyParser.urlencoded({ extended: true }));
+app.use(bodyParser.json());
+app.use(cookieParser());
+app.use(cors({ credentials: true, origin: "http://*:*" }));
+app.use(compression());
- const server = require("http").Server(app);
- const db = require("./db");
- const [socket, emit] = require("./sockets")(app, db, server);
- const passport = require("./passport")(db);
- const auth = passport.authenticate("jwt", { session: false });
+server = require("http").Server(app);
+require("./db");
+require("./lib/sockets");
+require("./lib/passport");
+require("./lib/rates");
- app.use(passport.initialize());
+require("./routes/payments");
+require("./routes/users");
- require("./rates")(app, socket);
+app.use((err, req, res, next) => {
+ l.info("res", res);
+ res.status(500);
+ res.send("An error occurred");
+ l.error("uncaught error", err.stack);
+ return res.end();
+});
- const seen = [];
- const addresses = {};
- await db.User.findAll({
- attributes: ["username", "address", "liquid"]
- }).map(u => {
- addresses[u.address] = u.username;
- if (u.liquid) addresses[u.liquid] = u.username;
- });
-
- const payments = (await db.Payment.findAll({
- attributes: ["hash"]
- })).map(p => p.hash);
-
- app.get("/rates", (req, res) => {
- res.send(app.get("rates"));
- });
-
- require("./payments")(app, auth, addresses, bc, db, emit, seen, payments);
- require("./users")(addresses, auth, app, bc, db, emit);
-
- app.use(function(err, req, res, next) {
- res.status(500);
- res.send("An error occurred");
- l.error(err.stack);
- return res.end();
- });
-
- server.listen(config.port, () =>
- console.log(`CoinOS Server listening on port ${config.port}`)
- );
-})();
+server.listen(config.port, () =>
+ console.log(`CoinOS Server listening on port ${config.port}`)
+);
diff --git a/binance.js b/lib/binance.js
index ccb010e1..2f4f7ef7 100644
--- a/binance.js
+++ b/lib/binance.js
@@ -1,8 +1,7 @@
const WebSocket = require("ws");
-const l = require("pino")();
-module.exports = (app, socket) => {
- const binance = new WebSocket(
+module.exports = () => {
+ binance = new WebSocket(
"wss://stream.binance.com:9443/ws/btcusdt@ticker"
);
diff --git a/devrates.js b/lib/devrates.js
index 817db79d..817db79d 100644
--- a/devrates.js
+++ b/lib/devrates.js
diff --git a/lib/passport.js b/lib/passport.js
new file mode 100644
index 00000000..1e4fd8b8
--- /dev/null
+++ b/lib/passport.js
@@ -0,0 +1,36 @@
+const Passport = require("passport");
+const jwt = require("passport-jwt");
+
+function cookieExtractor(req) {
+ if (req && req.cookies) return req.cookies["token"];
+ return null;
+}
+
+passport = Passport.use(
+ new jwt.Strategy(
+ {
+ jwtFromRequest: jwt.ExtractJwt.fromExtractors([
+ jwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
+ cookieExtractor
+ ]),
+ secretOrKey: config.jwt
+ },
+ async (payload, next) => {
+ try {
+ let user = await db.User.findOne({
+ where: {
+ username: payload.username
+ }
+ });
+ next(null, user);
+ } catch (e) {
+ l.error("error finding user for session", e);
+ next(null, { username: "guest" });
+ }
+ }
+ )
+);
+
+auth = passport.authenticate("jwt", { session: false });
+app.use(passport.initialize());
+
diff --git a/lib/rates.js b/lib/rates.js
new file mode 100644
index 00000000..702e586e
--- /dev/null
+++ b/lib/rates.js
@@ -0,0 +1,38 @@
+const axios = require("axios");
+
+let binance = require("./binance")();
+
+let fetchRates;
+(fetchRates = async () => {
+ let rates = require("./devrates");
+
+ if (process.env.NODE_ENV === "production") {
+ try {
+ let res = await axios.get(
+ `http://data.fixer.io/api/latest?access_key=${config.fixer}`
+ );
+
+ let fx = res.data.rates;
+
+ fx &&
+ Object.keys(fx).map(symbol => {
+ rates[symbol] = fx[symbol] / fx["USD"];
+ });
+ } catch (e) {
+ l.error("error fetching rates", e);
+ }
+ }
+
+ app.set("fxrates", rates);
+
+ setTimeout(fetchRates, 7200000);
+})();
+
+setInterval(() => {
+ socket.emit("rates", app.get("rates"));
+}, 1000);
+
+setInterval(() => {
+ binance.terminate();
+ binance = require("./binance")();
+}, 360000);
diff --git a/lib/sockets.js b/lib/sockets.js
new file mode 100644
index 00000000..3ebf86d7
--- /dev/null
+++ b/lib/sockets.js
@@ -0,0 +1,67 @@
+const jwt = require("jsonwebtoken");
+const io = require("socket.io");
+const Sequelize = require("sequelize");
+
+/* eslint-disable-next-line */
+const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
+const sids = {};
+
+socket = io(server, { origins: "*:*" });
+emit = (username, msg, data) => {
+ if (data.username !== undefined) data = pick(data, ...whitelist);
+
+ if (!sids[username]) return;
+ sids[username].map((sid, i) => {
+ try {
+ socket.to(sid).emit(msg, data);
+ } catch (e) {
+ sids[username].splice(i, 1);
+ }
+ });
+};
+
+socket.use((socket, next) => {
+ try {
+ let token = socket.handshake.query.token;
+ let user = jwt.decode(token).username;
+ socket.request.user = user;
+ sids[user] ? sids[user].push(socket.id) : (sids[user] = [socket.id]);
+ sids[socket.id] = user;
+ } catch (e) {
+ l.error("error creating socket", e);
+ }
+ next();
+});
+
+socket.sockets.on("connect", async socket => {
+ socket.emit("connected");
+ if (app.get("rates")) socket.emit("rate", app.get("rates").ask);
+ socket.on("getuser", async (data, callback) => {
+ l.info("socket open", socket.request.user);
+ const user = await db.User.findOne({
+ include: [
+ {
+ model: db.Payment,
+ as: "payments",
+ order: [["id", "DESC"]],
+ received: {
+ [Sequelize.Op.ne]: null
+ },
+ limit: 12
+ }
+ ],
+ where: {
+ username: socket.request.user
+ }
+ });
+
+ callback(user);
+ });
+
+ socket.on("disconnect", s => {
+ let user = sids[socket.id];
+ l.info("socket closed", user);
+ sids[user].splice(sids[user].indexOf(socket.id), 1);
+ delete sids[socket.id];
+ });
+});
diff --git a/whitelist.js b/lib/whitelist.js
index 23c46459..bdba4a95 100644
--- a/whitelist.js
+++ b/lib/whitelist.js
@@ -1,4 +1,4 @@
-module.exports = [
+whitelist = [
'id',
'fbtoken',
'username',
diff --git a/liquidPayments.js b/liquidPayments.js
deleted file mode 100644
index 3c2a12f4..00000000
--- a/liquidPayments.js
+++ /dev/null
@@ -1,145 +0,0 @@
-const reverse = require("buffer-reverse");
-const zmq = require("zeromq");
-const config = require("./config");
-const Sequelize = require("sequelize");
-const BitcoinCore = require("bitcoin-core");
-const bitcoin = require("elementsjs-lib");
-const l = require("pino")();
-
-const zmqRawBlock = zmq.socket("sub");
-zmqRawBlock.connect(config.liquid.zmqrawblock);
-zmqRawBlock.subscribe("rawblock");
-
-const zmqRawTx = zmq.socket("sub");
-zmqRawTx.connect("tcp://127.0.0.1:18603");
-zmqRawTx.subscribe("rawtx");
-
-let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
-const bc = new BitcoinCore(config.liquid);
-
-const toSats = n => parseInt((n * 100000000).toFixed())
-
-module.exports = (app, db, addresses, payments, emit) => {
- zmqRawTx.on("message", async (topic, message, sequence) => {
- const hex = message.toString("hex");
- const unblinded = await bc.unblindRawTransaction(hex);
- const tx = await bc.decodeRawTransaction(unblinded.hex);
- const blinded = await bc.decodeRawTransaction(hex);
- if (payments.includes(blinded.txid)) return;
-
- Promise.all(
- tx.vout.map(async o => {
- if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return;
- const value = toSats(o.value);
- const address = o.scriptPubKey.addresses[0];
-
- if (Object.keys(addresses).includes(address)) {
- let user = await db.User.findOne({
- where: {
- username: addresses[address]
- }
- });
-
- let invoices = await db.Payment.findAll({
- limit: 1,
- where: {
- address,
- received: null,
- amount: {
- [Sequelize.Op.gt]: 0
- }
- },
- order: [["id", "DESC"]]
- });
-
- let tip = null;
- if (invoices.length) tip = invoices[0].tip;
-
- let confirmed = 0;
-
- user.pending += value;
- user.confidential = await bc.getNewAddress();
- user.liquid = (await bc.getAddressInfo(user.confidential)).unconfidential;
-
- await user.save();
- emit(user.username, "user", user);
-
- addresses[user.liquid] = user.username;
-
- const payment = await db.Payment.create({
- user_id: user.id,
- hash: blinded.txid,
- amount: value,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- received: true,
- tip,
- confirmed,
- address,
- asset: 'LBTC',
- });
- payments.push(blinded.txid);
-
- l.info("liquid detected", user.username, value);
- emit(user.username, "payment", payment);
- }
- })
- );
- });
-
- let queue = {};
-
- zmqRawBlock.on("message", async (topic, message, sequence) => {
- const payments = await db.Payment.findAll({
- where: { confirmed: 0 }
- });
-
- const hashes = payments.map(p => p.hash);
-
- const block = bitcoin.Block.fromHex(message.toString("hex"), true);
- const hash = await bc.getBlockHash(block.height);
- const json = await bc.getBlock(hash, 2);
-
- json.tx.map(tx => {
- if (hashes.includes(tx.txid)) queue[tx.txid] = 1;
- });
- });
-
- setInterval(async () => {
- let arr = Object.keys(queue);
-
- for (let i = 0; i < arr.length; i++) {
- let hash = arr[i];
-
- let p = await db.Payment.findOne({
- include: [{ model: db.User, as: "user" }],
- where: { hash, confirmed: 0, received: 1 }
- })
-
- p.confirmed = 1;
-
- let user = await p.getUser();
- user.balance += p.amount;
- user.pending -= Math.min(user.pending, p.amount);
- l.info("liquid confirmed", user.username, p.amount);
- emit(user.username, "user", user);
-
- await user.save();
- await p.save();
-
- let payments = await db.Payment.findAll({
- where: {
- user_id: user.id,
- received: {
- [Sequelize.Op.ne]: null
- },
- },
- order: [['id', 'DESC']],
- limit: 12
- });
-
- emit(user.username, "payments", payments);
- delete queue[hash];
- }
- }, 1000);
-};
diff --git a/lnd.js b/lnd.js
deleted file mode 100644
index 2505475a..00000000
--- a/lnd.js
+++ /dev/null
@@ -1,26 +0,0 @@
-const fs = require('fs');
-const path = require('path');
-const grpc = require('grpc');
-const protoLoader = require('@grpc/proto-loader');
-
-module.exports = ({ tls, macaroon, server }) => {
- const packageDefinition = protoLoader.loadSync('rpc.proto', {
- keepCase: true,
- longs: String,
- enums: String,
- defaults: true,
- oneofs: true
- });
- const lnrpc = grpc.loadPackageDefinition(packageDefinition).lnrpc;
- process.env.GRPC_SSL_CIPHER_SUITES = 'HIGH+ECDSA'
- const lndCert = fs.readFileSync(tls);
- const sslCreds = grpc.credentials.createSsl(lndCert);
- const macaroonCreds = grpc.credentials.createFromMetadataGenerator(function(args, callback) {
- const macaroonFile = fs.readFileSync(macaroon).toString('hex');
- const metadata = new grpc.Metadata()
- metadata.add('macaroon', macaroonFile);
- callback(null, metadata);
- });
- const creds = grpc.credentials.combineChannelCredentials(sslCreds, macaroonCreds);
- return new lnrpc.Lightning(server, creds);
-}
diff --git a/migrations/20180128181238-create-user.js b/migrations/20180128181238-create-user.js
deleted file mode 100644
index 50424e18..00000000
--- a/migrations/20180128181238-create-user.js
+++ /dev/null
@@ -1,60 +0,0 @@
-'use strict';
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('users', {
- id: {
- allowNull: false,
- autoIncrement: true,
- primaryKey: true,
- type: Sequelize.INTEGER
- },
- username: {
- type: Sequelize.STRING
- },
- password: {
- type: Sequelize.STRING
- },
- email: {
- type: Sequelize.STRING
- },
- address: {
- type: Sequelize.STRING
- },
- unit: {
- type: Sequelize.STRING
- },
- symbol: {
- type: Sequelize.STRING
- },
- currency: {
- type: Sequelize.STRING
- },
- commission: {
- type: Sequelize.STRING
- },
- title: {
- type: Sequelize.STRING
- },
- pubkey: {
- type: Sequelize.STRING
- },
- privkey: {
- type: Sequelize.STRING
- },
- index: {
- type: Sequelize.INTEGER
- },
- createdAt: {
- allowNull: false,
- type: Sequelize.DATE
- },
- updatedAt: {
- allowNull: false,
- type: Sequelize.DATE
- }
- });
- },
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('users');
- }
-};
diff --git a/migrations/20180206033508-create-transaction.js b/migrations/20180206033508-create-transaction.js
deleted file mode 100644
index 06863a43..00000000
--- a/migrations/20180206033508-create-transaction.js
+++ /dev/null
@@ -1,39 +0,0 @@
-'use strict';
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('transactions', {
- id: {
- allowNull: false,
- autoIncrement: true,
- primaryKey: true,
- type: Sequelize.INTEGER
- },
- hash: {
- type: Sequelize.STRING
- },
- address: {
- type: Sequelize.STRING
- },
- date: {
- type: Sequelize.DATE
- },
- amount: {
- type: Sequelize.FLOAT
- },
- rate: {
- type: Sequelize.FLOAT
- },
- createdAt: {
- allowNull: false,
- type: Sequelize.DATE
- },
- updatedAt: {
- allowNull: false,
- type: Sequelize.DATE
- }
- });
- },
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('transactions');
- }
-};
diff --git a/migrations/20180214033702-addUserBalance.js b/migrations/20180214033702-addUserBalance.js
deleted file mode 100644
index 06ef5adb..00000000
--- a/migrations/20180214033702-addUserBalance.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.addColumn('users', 'balance', { type: Sequelize.INTEGER });
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.removeColumn('users', 'balance');
- }
-};
diff --git a/migrations/20180215052148-addChannelPoint.js b/migrations/20180215052148-addChannelPoint.js
deleted file mode 100644
index 3c175713..00000000
--- a/migrations/20180215052148-addChannelPoint.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.addColumn('users', 'channel', { type: Sequelize.STRING })
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.removeColumn('users', 'channel')
- }
-};
diff --git a/migrations/20180217065619-addChannelBalance.js b/migrations/20180217065619-addChannelBalance.js
deleted file mode 100644
index fc909f53..00000000
--- a/migrations/20180217065619-addChannelBalance.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.addColumn('users', 'channelbalance', { type: Sequelize.INTEGER })
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.removeColumn('users', 'channelbalance')
- }
-};
diff --git a/migrations/20180221010609-createInvoices.js b/migrations/20180221010609-createInvoices.js
deleted file mode 100644
index 3fbcfa21..00000000
--- a/migrations/20180221010609-createInvoices.js
+++ /dev/null
@@ -1,32 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('invoices', {
- id: {
- allowNull: false,
- autoIncrement: true,
- primaryKey: true,
- type: Sequelize.INTEGER,
- },
- user_id: {
- type: Sequelize.INTEGER,
- },
- payreq: {
- type: Sequelize.STRING,
- },
- createdAt: {
- allowNull: false,
- type: Sequelize.DATE
- },
- updatedAt: {
- allowNull: false,
- type: Sequelize.DATE
- },
- })
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('invoices')
- },
-};
diff --git a/migrations/20180312160019-addInvoiceRateAndCurrency.js b/migrations/20180312160019-addInvoiceRateAndCurrency.js
deleted file mode 100644
index 97dbc6ea..00000000
--- a/migrations/20180312160019-addInvoiceRateAndCurrency.js
+++ /dev/null
@@ -1,13 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- queryInterface.addColumn('invoices', 'rate', { type: Sequelize.DOUBLE })
- return queryInterface.addColumn('invoices', 'currency', { type: Sequelize.STRING })
- },
-
- down: (queryInterface, Sequelize) => {
- queryInterface.removeColumn('invoices', 'rate')
- return queryInterface.removeColumn('invoices', 'currency')
- }
-};
diff --git a/migrations/20180315232457-renameInvoices.js b/migrations/20180315232457-renameInvoices.js
deleted file mode 100644
index 9e06e56d..00000000
--- a/migrations/20180315232457-renameInvoices.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- queryInterface.renameTable('invoices', 'payments')
- queryInterface.addColumn('payments', 'address', { type: Sequelize.INTEGER });
- queryInterface.addColumn('payments', 'received', { type: Sequelize.BOOLEAN });
- },
-
- down: (queryInterface, Sequelize) => {
- queryInterface.removeColumn('payments', 'received');
- queryInterface.removeColumn('payments', 'address');
- queryInterface.renameTable('payments', 'invoices')
- }
-};
diff --git a/migrations/20180316032817-addPaymentsAmount.js b/migrations/20180316032817-addPaymentsAmount.js
deleted file mode 100644
index 89356ea3..00000000
--- a/migrations/20180316032817-addPaymentsAmount.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.addColumn('payments', 'amount', { type: Sequelize.DOUBLE });
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.removeColumn('payments', 'amount');
- }
-};
diff --git a/migrations/20180322163730-addTipToPayments.js b/migrations/20180322163730-addTipToPayments.js
deleted file mode 100644
index a482c7d4..00000000
--- a/migrations/20180322163730-addTipToPayments.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.addColumn('payments', 'tip', { type: Sequelize.DOUBLE })
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.removeColumn('payments', 'tip');
- }
-};
diff --git a/migrations/20181008052713-addUserChannels.js b/migrations/20181008052713-addUserChannels.js
deleted file mode 100644
index 29692445..00000000
--- a/migrations/20181008052713-addUserChannels.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('users_channels', { user_id: Sequelize.INTEGER, channel_id: Sequelize.INTEGER });
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('users_channels');
- }
-};
diff --git a/migrations/20181008053428-addUsersBalances.js b/migrations/20181008053428-addUsersBalances.js
deleted file mode 100644
index 5f528322..00000000
--- a/migrations/20181008053428-addUsersBalances.js
+++ /dev/null
@@ -1,11 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('users_balances', { user_id: Sequelize.INTEGER, symbol: Sequelize.STRING, balance: Sequelize.INTEGER, pending: Sequelize.INTEGER });
- },
-
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('users_balances');
- }
-};
diff --git a/migrations/20190113193839-create_orders.js b/migrations/20190113193839-create_orders.js
deleted file mode 100644
index 01e1ffb1..00000000
--- a/migrations/20190113193839-create_orders.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use strict';
-
-module.exports = {
- up: (queryInterface, Sequelize) => {
- return queryInterface.createTable('orders', {
- id: {
- allowNull: false,
- autoIncrement: true,
- primaryKey: true,
- type: Sequelize.INTEGER
- },
- amount: { type: Sequelize.INTEGER },
- price: { type: Sequelize.INTEGER },
- type: { type: Sequelize.STRING },
- pair: { type: Sequelize.STRING },
- createdAt: {
- allowNull: false,
- type: Sequelize.DATE
- },
- });
- },
- down: (queryInterface, Sequelize) => {
- return queryInterface.dropTable('orders');
- },
-};
diff --git a/models/payments.js b/models/payments.js
deleted file mode 100644
index 213c11b4..00000000
--- a/models/payments.js
+++ /dev/null
@@ -1,141 +0,0 @@
-const {
- DataTypes
-} = require('sequelize');
-
-module.exports = sequelize => {
- const attributes = {
- id: {
- type: DataTypes.INTEGER(11),
- allowNull: false,
- defaultValue: null,
- primaryKey: true,
- autoIncrement: true,
- comment: null,
- field: "id"
- },
- user_id: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "user_id"
- },
- hash: {
- type: DataTypes.TEXT,
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "hash"
- },
- createdAt: {
- type: DataTypes.DATE,
- allowNull: false,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "createdAt"
- },
- updatedAt: {
- type: DataTypes.DATE,
- allowNull: false,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "updatedAt"
- },
- rate: {
- type: DataTypes.DOUBLE,
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "rate"
- },
- asset: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "asset"
- },
- currency: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "currency"
- },
- address: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "address"
- },
- received: {
- type: DataTypes.INTEGER(1),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "received"
- },
- amount: {
- type: DataTypes.DOUBLE,
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "amount"
- },
- fee: {
- type: DataTypes.DOUBLE,
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "fee"
- },
- tip: {
- type: DataTypes.DOUBLE,
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "tip"
- },
- confirmed: {
- type: DataTypes.INTEGER(1),
- allowNull: false,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "confirmed"
- }
- };
- const options = {
- tableName: "payments",
- comment: "",
- indexes: []
- };
- const PaymentsModel = sequelize.define("payments_model", attributes, options);
- return PaymentsModel;
-};
diff --git a/models/users.js b/models/users.js
deleted file mode 100644
index 7adeeaf6..00000000
--- a/models/users.js
+++ /dev/null
@@ -1,249 +0,0 @@
-const {
- DataTypes
-} = require('sequelize');
-
-module.exports = sequelize => {
- const attributes = {
- id: {
- type: DataTypes.INTEGER(11),
- allowNull: false,
- defaultValue: null,
- primaryKey: true,
- autoIncrement: true,
- comment: null,
- field: "id"
- },
- username: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "username"
- },
- password: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "password"
- },
- liquid: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "liquid"
- },
- confidential: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "confidential"
- },
- address: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "address"
- },
- symbol: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "symbol"
- },
- otpsecret: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: "CAD",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "otpsecret"
- },
- currency: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: "CAD",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "currency"
- },
- currencies: {
- type: DataTypes.JSON,
- allowNull: false,
- defaultValue: ["USD"],
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "currencies"
- },
- pubkey: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pubkey"
- },
- privkey: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "privkey"
- },
- createdAt: {
- type: DataTypes.DATE,
- allowNull: false,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "createdAt"
- },
- updatedAt: {
- type: DataTypes.DATE,
- allowNull: false,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "updatedAt"
- },
- index: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "index"
- },
- balance: {
- type: DataTypes.INTEGER(11),
- allowNull: false,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "balance"
- },
- channel: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "channel"
- },
- readonlypw: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "readonlypw"
- },
- limit: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "limit"
- },
- fbtoken: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "fbtoken"
- },
- friend: {
- type: DataTypes.INTEGER(1),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "friend"
- },
- pending: {
- type: DataTypes.INTEGER(11),
- allowNull: true,
- defaultValue: "0",
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pending"
- },
- name: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "name"
- },
- pic: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pic"
- },
- twofa: {
- type: DataTypes.INTEGER(1),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "twofa"
- },
- pin: {
- type: DataTypes.STRING(255),
- allowNull: true,
- defaultValue: null,
- primaryKey: false,
- autoIncrement: false,
- comment: null,
- field: "pin"
- },
- };
- const options = {
- tableName: "users",
- comment: "",
- indexes: []
- };
- const UsersModel = sequelize.define("users_model", attributes, options);
- return UsersModel;
-};
diff --git a/passport.js b/passport.js
deleted file mode 100644
index df72c6a5..00000000
--- a/passport.js
+++ /dev/null
@@ -1,39 +0,0 @@
-const passport = require("passport");
-const jwt = require("passport-jwt");
-const config = require("./config");
-
-const l = require("pino")();
-
-function cookieExtractor(req) {
- if (req && req.cookies) return req.cookies["token"];
- return null;
-}
-
-module.exports = db => {
- passport.use(
- new jwt.Strategy(
- {
- jwtFromRequest: jwt.ExtractJwt.fromExtractors([
- jwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
- cookieExtractor
- ]),
- secretOrKey: config.jwt
- },
- async (payload, next) => {
- try {
- let user = await db.User.findOne({
- where: {
- username: payload.username
- }
- });
- next(null, user);
- } catch (e) {
- l.error("error finding user for session", e);
- next(null, { username: "guest" });
- }
- }
- )
- );
-
- return passport;
-};
diff --git a/payments.js b/payments.js
deleted file mode 100644
index c2d9d306..00000000
--- a/payments.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const createLnrpc = require("./lib/createLnrpc");
-const config = require("./config");
-const lnd = require("./lnd");
-
-module.exports = async (app, auth, addresses, bc, db, emit, seen, payments) => {
- const lna = await createLnrpc(config.lna);
- const lnb = await createLnrpc(config.lnb);
-
- const lnaraw = lnd(config.lna);
- const lnbraw = lnd(config.lnb);
-
- require("./bitcoinPayments")(app, bc, db, addresses, payments, emit);
- require("./liquidPayments")(app, db, addresses, payments, emit);
- require("./lightningPayments")(app, db, lna, lnb, emit, payments);
-
- app.post(
- "/queryRoutes",
- auth,
- require("./queryRoutes")(lnaraw)
- );
- app.post(
- "/sendPayment",
- auth,
- require("./sendPayment")(app, db, emit, seen, lnaraw, lnb)
- );
- app.post("/payUser", auth, require("./payUser")(app, db, lna, lnb));
- app.post("/sendCoins", auth, require("./sendCoins")(addresses, app, bc, db, emit));
- app.post("/estimateBitcoinFee", auth, require("./estimateBitcoinFee")(app, bc, db, emit));
- app.post("/sendLiquid", auth, require("./sendLiquid")(addresses, app, db, emit));
- app.post("/estimateLiquidFee", auth, require("./estimateLiquidFee")(app, db, emit));
- app.post("/addInvoice", auth, require("./addInvoice")(app, db, lnb));
- app.get("/payments", auth, async (req, res) => {
- const payments = await db.Payment.findAll({
- where: { user_id: req.user.id },
- order: [['id', 'DESC']],
- });
-
- emit(req.user.username, "payments", payments);
- res.send(payments);
- });
-};
diff --git a/queryRoutes.js b/queryRoutes.js
deleted file mode 100644
index 1811f7c4..00000000
--- a/queryRoutes.js
+++ /dev/null
@@ -1,13 +0,0 @@
-const bolt11 = require("bolt11");
-const config = require("./config");
-const l = require("pino")();
-
-module.exports = lna => async (req, res) => {
- let hash = req.body.payreq;
- let payreq = bolt11.decode(hash);
- lna.queryRoutes(
- { pub_key: payreq.payeeNodeKey, amt: payreq.satoshis },
- (err, response) => res.send(response)
-
- );
-};
diff --git a/rates.js b/rates.js
deleted file mode 100644
index eda6dec4..00000000
--- a/rates.js
+++ /dev/null
@@ -1,42 +0,0 @@
-const axios = require("axios");
-const config = require("./config");
-const l = require("pino")();
-
-module.exports = (app, socket) => {
- let binance = require("./binance")(app, socket);
-
- let fetchRates;
- (fetchRates = async () => {
- let rates = require("./devrates");
-
- if (process.env.NODE_ENV === "production") {
- try {
- let res = await axios.get(
- `http://data.fixer.io/api/latest?access_key=${config.fixer}`
- );
-
- let fx = res.data.rates;
-
- fx &&
- Object.keys(fx).map(symbol => {
- rates[symbol] = fx[symbol] / fx["USD"];
- });
- } catch (e) {
- l.error("error fetching rates", e);
- }
- }
-
- app.set("fxrates", rates);
-
- setTimeout(fetchRates, 7200000);
- })();
-
- setInterval(() => {
- socket.emit("rates", app.get("rates"));
- }, 1000);
-
- setInterval(() => {
- binance.terminate();
- binance = require("./binance")(app, socket);
- }, 360000);
-};
diff --git a/estimateBitcoinFee.js b/routes/bitcoin/fee.js
index b32a0dc6..259f2329 100644
--- a/estimateBitcoinFee.js
+++ b/routes/bitcoin/fee.js
@@ -1,9 +1,4 @@
-const config = require("./config");
-
-const l = require("pino")();
-const SATS = 100000000;
-
-module.exports = (app, bc, db, emit) => async (req, res) => {
+module.exports = async (req, res) => {
let { user } = req;
let { address, amount, feeRate } = req.body;
let tx, fee;
@@ -24,7 +19,7 @@ module.exports = (app, bc, db, emit) => async (req, res) => {
tx = await bc.fundRawTransaction(tx, params);
res.send({ tx });
} catch (e) {
- l.error(e);
+ l.error("bitcoin fee estimation error", e);
return res.status(500).send(e.message);
}
};
diff --git a/routes/bitcoin/receive.js b/routes/bitcoin/receive.js
new file mode 100644
index 00000000..b5ab4298
--- /dev/null
+++ b/routes/bitcoin/receive.js
@@ -0,0 +1,143 @@
+const reverse = require("buffer-reverse");
+const zmq = require("zeromq");
+const { Op } = require("sequelize");
+
+const bitcoin = require("bitcoinjs-lib");
+
+const zmqRawBlock = zmq.socket("sub");
+zmqRawBlock.connect(config.bitcoin.zmqrawblock);
+zmqRawBlock.subscribe("rawblock");
+
+const zmqRawTx = zmq.socket("sub");
+zmqRawTx.connect(config.bitcoin.zmqrawtx);
+zmqRawTx.subscribe("rawtx");
+
+let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
+
+zmqRawTx.on("message", async (topic, message, sequence) => {
+ const hex = message.toString("hex");
+ let tx = bitcoin.Transaction.fromHex(message);
+ let hash = reverse(tx.getHash()).toString("hex");
+
+ if (payments.includes(hash)) return;
+
+ Promise.all(
+ tx.outs.map(async o => {
+ const { value } = o;
+
+ let address;
+ try {
+ address = bitcoin.address.fromOutputScript(
+ o.script,
+ NETWORK
+ );
+ } catch (e) {
+ return;
+ }
+
+ if (Object.keys(addresses).includes(address)) {
+ payments.push(hash);
+
+ let user = await db.User.findOne({
+ where: {
+ username: addresses[address]
+ }
+ });
+
+ let invoices = await db.Payment.findAll({
+ limit: 1,
+ where: {
+ address,
+ received: null,
+ amount: {
+ [Op.gt]: 0
+ }
+ },
+ order: [["createdAt", "DESC"]]
+ });
+
+ let tip = null;
+ if (invoices.length) tip = invoices[0].tip;
+
+ let confirmed = false;
+
+ user.address = await bc.getNewAddress("", "bech32");
+ user.pending += value;
+
+ await user.save();
+ emit(user.username, "user", user);
+
+ addresses[user.address] = user.username;
+
+ const payment = await db.Payment.create({
+ user_id: user.id,
+ hash,
+ amount: value,
+ currency: user.currency,
+ rate: app.get("rates")[user.currency],
+ received: true,
+ tip,
+ confirmed,
+ address,
+ asset: 'BTC',
+ });
+
+ l.info("bitcoin detected", user.username, o.value);
+ emit(user.username, "payment", payment);
+ }
+ })
+ );
+});
+
+let queue = {};
+
+zmqRawBlock.on("message", async (topic, message, sequence) => {
+ const payments = await db.Payment.findAll({
+ where: { confirmed: false }
+ });
+
+ const hashes = payments.map(p => p.hash);
+
+ let block = bitcoin.Block.fromHex(message.toString("hex"));
+ block.transactions.map(tx => {
+ let hash = reverse(tx.getHash()).toString("hex");
+ if (hashes.includes(hash)) queue[hash] = 1;
+ });
+});
+
+setInterval(async () => {
+ let arr = Object.keys(queue);
+ for (let i = 0; i < arr.length; i++) {
+ let hash = arr[i];
+
+ let p = await db.Payment.findOne({
+ include: [{ model: db.User, as: "user" }],
+ where: { hash, confirmed: 0 }
+ })
+
+ p.confirmed = 1;
+
+ let user = await p.getUser();
+ user.balance += p.amount;
+ user.pending -= Math.min(user.pending, p.amount);
+ emit(user.username, "user", user);
+
+ await user.save();
+ await p.save();
+
+ let payments = await db.Payment.findAll({
+ where: {
+ user_id: user.id,
+ received: {
+ [Op.ne]: null
+ },
+ },
+ order: [['id', 'DESC']],
+ limit: 12
+ });
+
+ l.info("bitcoin confirmed", user.username, p.amount);
+ emit(user.username, "payments", payments);
+ delete queue[hash];
+ }
+}, 1000);
diff --git a/sendCoins.js b/routes/bitcoin/send.js
index 1c9f6937..3c545178 100644
--- a/sendCoins.js
+++ b/routes/bitcoin/send.js
@@ -1,11 +1,7 @@
const bitcoin = require("bitcoinjs-lib");
-const config = require("./config");
const reverse = require("buffer-reverse");
-const l = require("pino")();
-const toSats = n => parseInt((n * 100000000).toFixed())
-
-module.exports = (addresses, app, bc, db, emit) => async (req, res) => {
+module.exports = async (req, res) => {
let { user } = req;
let { address, tx } = req.body;
let { hex } = tx;
@@ -55,7 +51,7 @@ module.exports = (addresses, app, bc, db, emit) => async (req, res) => {
await user.save({ transaction });
});
} catch (e) {
- l.error(e);
+ l.warn("insufficient funds for bitcoin payment", e);
return res.status(500).send("Not enough satoshis");
}
@@ -91,7 +87,7 @@ module.exports = (addresses, app, bc, db, emit) => async (req, res) => {
res.send(payment);
});
} catch (e) {
- l.error(e);
+ l.error("error sending bitcoin", e);
return res.status(500).send(e.message);
}
};
diff --git a/addInvoice.js b/routes/lightning/invoice.js
index fa183321..59f0df47 100644
--- a/addInvoice.js
+++ b/routes/lightning/invoice.js
@@ -1,4 +1,4 @@
-module.exports = (app, db, lnb) => async (req, res) => {
+module.exports = async (req, res) => {
let err = m => res.status(500).send(m);
let { amount, address, tip } = req.body;
diff --git a/routes/lightning/query.js b/routes/lightning/query.js
new file mode 100644
index 00000000..8e25293c
--- /dev/null
+++ b/routes/lightning/query.js
@@ -0,0 +1,7 @@
+const bolt11 = require("bolt11");
+
+module.exports = async (req, res) => {
+ let hash = req.body.payreq;
+ let payreq = bolt11.decode(hash);
+ res.send(await lna.queryRoutes({ pub_key: payreq.payeeNodeKey, amt: payreq.satoshis }));
+};
diff --git a/lightningPayments.js b/routes/lightning/receive.js
index 096655bf..6befc7a3 100644
--- a/lightningPayments.js
+++ b/routes/lightning/receive.js
@@ -1,6 +1,4 @@
-const l = require("pino")();
-
-module.exports = (app, db, lna, lnb, emit, payments) => {
+module.exports = {
const handlePayment = async msg => {
if (!msg.settled) return;
diff --git a/routes/lightning/send.js b/routes/lightning/send.js
new file mode 100644
index 00000000..2b661b60
--- /dev/null
+++ b/routes/lightning/send.js
@@ -0,0 +1,93 @@
+const bolt11 = require("bolt11");
+
+module.exports = async (req, res) => {
+ let hash = req.body.payreq;
+ let payreq = bolt11.decode(hash);
+ let { route } = req.body;
+ let { user } = req;
+
+ if (seen.includes(hash)) {
+ l.warn("attempted to pay a paid invoice", user.username);
+ return res.status(500).send("Invoice has been paid, can't pay again");
+ }
+
+ try {
+ await db.transaction(async transaction => {
+ let { balance } = await db.User.findOne(
+ {
+ where: {
+ username: user.username
+ }
+ },
+ { transaction }
+ );
+
+ if (balance < payreq.satoshis) {
+ throw new Error("insufficient funds");
+ }
+
+ user.balance -= payreq.satoshis;
+ await user.save({ transaction });
+ });
+ } catch (e) {
+ l.warn("insufficient funds for lightning payment", e);
+ return res.status(500).send("Not enough satoshis");
+ }
+
+ let m;
+ try {
+ let paymentHash = payreq.tags.find(t => t.tagName === 'payment_hash').data;
+ m = await lna.sendToRouteSync({
+ "payment_hash": Buffer.from(paymentHash, 'hex'),
+ route
+ });
+
+ if (m.payment_error) return res.status(500).send(m.payment_error);
+ if (seen.includes(m.payment_preimage)) return;
+ seen.push(m.payment_preimage);
+
+ let total = parseInt(m.payment_route.total_amt);
+ let fee = m.payment_route.total_fees;
+
+ user.balance -= total - payreq.satoshis;
+
+ await db.transaction(async transaction => {
+ await user.save({ transaction });
+
+ const payment = await db.Payment.create(
+ {
+ amount: -total,
+ fee,
+ user_id: user.id,
+ hash,
+ rate: app.get("rates")[user.currency],
+ currency: user.currency,
+ confirmed: true,
+ asset: 'LNBTC',
+ },
+ { transaction }
+ );
+
+ l.info("sent lightning", user.username, -payment.amount);
+ emit(user.username, "payment", payment);
+ emit(user.username, "user", user);
+
+ if (payreq.payeeNodeKey === config.lnb.id) {
+ lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => {
+ let payback = lnb.sendPayment(lnb.meta, {});
+
+ /* eslint-disable-next-line */
+ let { payment_request } = invoice;
+ /* eslint-disable-next-line */
+ payback.write({ payment_request });
+ });
+ }
+
+ seen.push(hash);
+ res.send(payment);
+ });
+ } catch (e) {
+ l.info("error sending lightning", e);
+ return res.status(500).send(e);
+ }
+};
diff --git a/payUser.js b/routes/lightning/user.js
index 676b0093..c361600c 100644
--- a/payUser.js
+++ b/routes/lightning/user.js
@@ -1,6 +1,6 @@
const bolt11 = require("bolt11");
-module.exports = (app, db, lna, lnb) => async (req, res) => {
+module.exports = async (req, res) => {
let { payuser, amount } = req.body;
let user = await db.User.findOne({
diff --git a/estimateLiquidFee.js b/routes/liquid/fee.js
index fb9e54f0..402c0b2a 100644
--- a/estimateLiquidFee.js
+++ b/routes/liquid/fee.js
@@ -1,12 +1,4 @@
-const config = require("./config");
-const BitcoinCore = require("bitcoin-core");
-
-const l = require("pino")();
-const SATS = 100000000;
-
-const bc = new BitcoinCore(config.liquid);
-
-module.exports = (app, db, emit) => async (req, res) => {
+module.exports = async (req, res) => {
let { user } = req;
let { address, amount, feeRate } = req.body;
let tx, fee;
@@ -14,7 +6,7 @@ module.exports = (app, db, emit) => async (req, res) => {
try {
amount = parseInt(amount);
- tx = await bc.createRawTransaction([], {
+ tx = await lq.createRawTransaction([], {
[address]: (amount / SATS).toFixed(8)
});
@@ -24,10 +16,10 @@ module.exports = (app, db, emit) => async (req, res) => {
if (feeRate) params["feeRate"] = (parseInt(feeRate) / SATS * 1000).toFixed(8);
- tx = await bc.fundRawTransaction(tx, params);
+ tx = await lq.fundRawTransaction(tx, params);
res.send({ tx });
} catch (e) {
- l.error(e);
+ l.error("error estimating liquid fee", e);
return res.status(500).send(e.message);
}
};
diff --git a/routes/liquid/receive.js b/routes/liquid/receive.js
new file mode 100644
index 00000000..2e622aad
--- /dev/null
+++ b/routes/liquid/receive.js
@@ -0,0 +1,137 @@
+const reverse = require("buffer-reverse");
+const zmq = require("zeromq");
+const { Op } = require("sequelize");
+const bitcoin = require("elementsjs-lib");
+
+const zmqRawBlock = zmq.socket("sub");
+zmqRawBlock.connect(config.liquid.zmqrawblock);
+zmqRawBlock.subscribe("rawblock");
+
+const zmqRawTx = zmq.socket("sub");
+zmqRawTx.connect("tcp://127.0.0.1:18603");
+zmqRawTx.subscribe("rawtx");
+
+let NETWORK = bitcoin.networks[config.bitcoin.network === "mainnet" ? "bitcoin" : config.bitcoin.network];
+
+zmqRawTx.on("message", async (topic, message, sequence) => {
+ const hex = message.toString("hex");
+ const unblinded = await lq.unblindRawTransaction(hex);
+ const tx = await lq.decodeRawTransaction(unblinded.hex);
+ const blinded = await lq.decodeRawTransaction(hex);
+ if (payments.includes(blinded.txid)) return;
+
+ Promise.all(
+ tx.vout.map(async o => {
+ if (!(o.scriptPubKey && o.scriptPubKey.addresses)) return;
+ const value = toSats(o.value);
+ const address = o.scriptPubKey.addresses[0];
+
+ if (Object.keys(addresses).includes(address)) {
+ let user = await db.User.findOne({
+ where: {
+ username: addresses[address]
+ }
+ });
+
+ let invoices = await db.Payment.findAll({
+ limit: 1,
+ where: {
+ address,
+ received: null,
+ amount: {
+ [Op.gt]: 0
+ }
+ },
+ order: [["id", "DESC"]]
+ });
+
+ let tip = null;
+ if (invoices.length) tip = invoices[0].tip;
+
+ let confirmed = 0;
+
+ user.pending += value;
+ user.confidential = await lq.getNewAddress();
+ user.liquid = (await lq.getAddressInfo(user.confidential)).unconfidential;
+
+ await user.save();
+ emit(user.username, "user", user);
+
+ addresses[user.liquid] = user.username;
+
+ const payment = await db.Payment.create({
+ user_id: user.id,
+ hash: blinded.txid,
+ amount: value,
+ currency: user.currency,
+ rate: app.get("rates")[user.currency],
+ received: true,
+ tip,
+ confirmed,
+ address,
+ asset: 'LBTC',
+ });
+ payments.push(blinded.txid);
+
+ l.info("liquid detected", user.username, value);
+ emit(user.username, "payment", payment);
+ }
+ })
+ );
+});
+
+let queue = {};
+
+zmqRawBlock.on("message", async (topic, message, sequence) => {
+ const payments = await db.Payment.findAll({
+ where: { confirmed: 0 }
+ });
+
+ const hashes = payments.map(p => p.hash);
+
+ const block = bitcoin.Block.fromHex(message.toString("hex"), true);
+ const hash = await lq.getBlockHash(block.height);
+ const json = await lq.getBlock(hash, 2);
+
+ json.tx.map(tx => {
+ if (hashes.includes(tx.txid)) queue[tx.txid] = 1;
+ });
+});
+
+setInterval(async () => {
+ let arr = Object.keys(queue);
+
+ for (let i = 0; i < arr.length; i++) {
+ let hash = arr[i];
+
+ let p = await db.Payment.findOne({
+ include: [{ model: db.User, as: "user" }],
+ where: { hash, confirmed: 0, received: 1 }
+ })
+
+ p.confirmed = 1;
+
+ let user = await p.getUser();
+ user.balance += p.amount;
+ user.pending -= Math.min(user.pending, p.amount);
+ l.info("liquid confirmed", user.username, p.amount);
+ emit(user.username, "user", user);
+
+ await user.save();
+ await p.save();
+
+ let payments = await db.Payment.findAll({
+ where: {
+ user_id: user.id,
+ received: {
+ [Op.ne]: null
+ },
+ },
+ order: [['id', 'DESC']],
+ limit: 12
+ });
+
+ emit(user.username, "payments", payments);
+ delete queue[hash];
+ }
+}, 1000);
diff --git a/sendLiquid.js b/routes/liquid/send.js
index a86395a2..aaced4a9 100644
--- a/sendLiquid.js
+++ b/routes/liquid/send.js
@@ -1,14 +1,6 @@
-const bitcoin = require("bitcoinjs-lib");
-const config = require("./config");
const reverse = require("buffer-reverse");
-const BitcoinCore = require("bitcoin-core");
-const l = require("pino")();
-const toSats = n => parseInt((n * 100000000).toFixed())
-
-const bc = new BitcoinCore(config.liquid);
-
-module.exports = (addresses, app, db, emit) => async (req, res) => {
+module.exports = async (req, res) => {
let { user } = req;
let {
address,
@@ -61,7 +53,7 @@ module.exports = (addresses, app, db, emit) => async (req, res) => {
await user.save({ transaction });
});
} catch (e) {
- l.error("balance check failed", e);
+ l.warn("insufficient funds for liquid payment", e);
return res.status(500).send("Not enough satoshis");
}
diff --git a/routes/payments.js b/routes/payments.js
new file mode 100644
index 00000000..d5038b08
--- /dev/null
+++ b/routes/payments.js
@@ -0,0 +1,40 @@
+const BitcoinCore = require("bitcoin-core");
+const createLnrpc = require("../lib/createLnrpc");
+
+(async () => {
+ bc = new BitcoinCore(config.bitcoin);
+ lq = new BitcoinCore(config.liquid);
+
+ lna = await createLnrpc(config.lna);
+ lnb = await createLnrpc(config.lnb);
+
+ seen = [];
+ addresses = {};
+ await db.User.findAll({
+ attributes: ["username", "address", "liquid"]
+ }).map(u => {
+ addresses[u.address] = u.username;
+ if (u.liquid) addresses[u.liquid] = u.username;
+ });
+
+ app.post("/lightning/invoice", auth, require("./lightning/invoice"));
+ app.post("/lightning/query", auth, require("./lightning/query"));
+ app.post("/lightning/send", auth, require("./lightning/send"));
+ app.post("/lightning/user", auth, require("./lightning/user"));
+
+ app.post("/bitcoin/send", auth, require("./bitcoin/send"));
+ app.post("/bitcoin/fee", auth, require("./bitcoin/fee"));
+
+ app.post("/liquid/send", auth, require("./liquid/send"));
+ app.post("/liquid/fee", auth, require("./liquid/fee"));
+
+ app.get("/payments", auth, async (req, res) => {
+ const payments = await db.Payment.findAll({
+ where: { user_id: req.user.id },
+ order: [["id", "DESC"]]
+ });
+
+ emit(req.user.username, "payments", payments);
+ res.send(payments);
+ });
+})();
diff --git a/routes/users.js b/routes/users.js
new file mode 100644
index 00000000..4d9127e1
--- /dev/null
+++ b/routes/users.js
@@ -0,0 +1,328 @@
+const axios = require("axios");
+const bcrypt = require("bcrypt");
+const jwt = require("jsonwebtoken");
+const uuidv4 = require("uuid/v4");
+const fb = "https://graph.facebook.com/";
+const authenticator = require("otplib").authenticator;
+
+const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
+let faucet = 1000;
+const DAY = 24 * 60 * 60 * 1000;
+require("../lib/whitelist");
+
+const twofa = (req, res, next) => {
+ let {
+ user,
+ body: { token }
+ } = req;
+ if (!user.twofa || authenticator.check(token, user.otpsecret)) next(req, res);
+ else res.status(401).send("2fa required");
+};
+
+
+app.get("/liquidate", async (req, res) => {
+ let users = await db.User.findAll();
+ for (let i = 0; i < users.length; i++) {
+ let user = users[i];
+ if (!user.confidential) {
+ user.confidential = await lq.getNewAddress();
+ user.liquid = (await lq.getAddressInfo(
+ user.confidential
+ )).unconfidential;
+ await user.save();
+ }
+
+ if (!user.otpsecret) {
+ user.otpsecret = authenticator.generateSecret();
+ await user.save();
+ }
+ }
+ res.end();
+});
+
+const gift = async user => {
+ if (faucet > 0) {
+ user.balance = 100;
+ faucet -= 100;
+ const payment = await db.Payment.create({
+ user_id: user.id,
+ hash: "Welcome Gift",
+ amount: 100,
+ currency: user.currency,
+ rate: app.get("rates")[user.currency],
+ received: true,
+ confirmed: 1,
+ asset: "GIFT"
+ });
+ await user.save();
+ }
+};
+
+app.post("/register", async (req, res) => {
+ let err = m => res.status(500).send(m);
+ let user = req.body;
+ if (!user.username) return err("Username required");
+
+ let exists = await db.User.count({ where: { username: user.username } });
+ if (exists) return err("Username taken");
+
+ user.address = await bc.getNewAddress("", "bech32");
+ user.confidential = await lq.getNewAddress();
+ user.liquid = (await lq.getAddressInfo(
+ user.confidential
+ )).unconfidential;
+ user.name = user.username;
+ let ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
+ let countries = {
+ CA: "CAD",
+ US: "USD",
+ JP: "JPY",
+ CN: "CNY",
+ AU: "AUD",
+ GB: "GBP"
+ };
+
+ if (ip.startsWith('127') || ip.startsWith('192')) user.currency = "CAD";
+ else {
+ let info = await axios.get(`http://api.ipstack.com/${ip}?access_key=${config.ipstack}`);
+ user.currency = countries[info.data.country_code] || "USD";
+ }
+
+ user.currencies = [user.currency];
+ user.otpsecret = authenticator.generateSecret();
+
+ addresses[user.address] = user.username;
+ addresses[user.liquid] = user.username;
+ user = await db.User.create(user);
+ await gift(user);
+
+ l.info("new user", user.username);
+ res.send(pick(user, ...whitelist));
+});
+
+app.post("/disable2fa", auth, twofa, async (req, res) => {
+ let { user } = req;
+ user.twofa = false;
+ await user.save();
+ emit(user.username, "user", user);
+ emit(user.username, "otpsecret", user.otpsecret);
+ l.info("disabled 2fa", user.username);
+ res.end();
+});
+
+app.get("/otpsecret", auth, twofa, async (req, res) => {
+ let { user } = req;
+ emit(user.username, "otpsecret", user.otpsecret);
+ res.end();
+});
+
+app.post("/2fa", auth, async (req, res) => {
+ let { user } = req;
+ try {
+ const isValid = authenticator.check(req.body.token, req.user.otpsecret);
+ if (isValid) {
+ user.twofa = true;
+ user.save();
+ emit(user.username, "user", req.user);
+ } else {
+ return res.status(500).send("Invalid token");
+ }
+ } catch (e) {
+ l.error("error setting up 2fa", e);
+ }
+
+ l.info("enabled 2fa", user.username);
+ res.end();
+});
+
+app.post("/user", auth, async (req, res) => {
+ try {
+ let { user } = req;
+ let {
+ username,
+ currency,
+ currencies,
+ twofa,
+ pin,
+ password,
+ passconfirm
+ } = req.body;
+
+ let exists = await db.User.findOne({
+ where: { username }
+ });
+
+ let token;
+ if (user.username !== username && exists) {
+ return res.status(500).send("Username taken");
+ } else {
+ user.username = username;
+ addresses[user.address] = username;
+ addresses[user.liquid] = user.username;
+ token = jwt.sign({ username }, config.jwt);
+ res.cookie("token", token, {
+ expires: new Date(Date.now() + 432000000)
+ });
+ }
+
+ user.currencies = currencies;
+ user.currency = currency;
+ if (currencies.length) {
+ if (!currencies.includes(currency)) user.currency = currencies[0];
+ } else {
+ user.currency = null;
+ }
+
+ user.twofa = twofa;
+ user.pin = pin;
+
+ if (password && password === passconfirm) {
+ user.password = await bcrypt.hash(password, 1);
+ }
+
+ await user.save();
+ emit(user.username, "user", req.user);
+ res.send({ user, token });
+ } catch (e) {
+ l.error("error updating user", e);
+ }
+});
+
+app.post("/login", async (req, res) => {
+ let twofa = req.body.token;
+
+ try {
+ let user = await db.User.findOne({
+ where: {
+ username: req.body.username
+ }
+ });
+
+ if (
+ !user ||
+ (user.password &&
+ !(await bcrypt.compare(req.body.password, user.password)))
+ ) {
+ l.warn("invalid username or password attempt");
+ return res.status(401).end();
+ }
+
+ if (user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret))) {
+ return res.status(401).send("2fa required");
+ }
+
+ let payload = { username: user.username };
+ let token = jwt.sign(payload, config.jwt);
+ res.cookie("token", token, { expires: new Date(Date.now() + 432000000) });
+
+ user = pick(user, ...whitelist);
+ res.send({ user, token });
+ } catch (e) {
+ l.error("login error", e);
+ res.status(401).end();
+ }
+});
+
+app.post("/facebookLogin", async (req, res) => {
+ let { accessToken, userID } = req.body;
+ let twofa = req.body.token;
+
+ let url = `https://graph.facebook.com/debug_token?input_token=${accessToken}&access_token=${
+ config.facebook.appToken
+ }`;
+ let check = await axios.get(url);
+ if (!check.data.data.is_valid) return res.status(401).end();
+
+ try {
+ let user = await db.User.findOne({
+ where: {
+ username: req.body.userID
+ }
+ });
+
+ if (!user) {
+ user = await db.User.create(user);
+ user.username = userID;
+ user.name = (await axios.get(
+ `${fb}/me?access_token=${accessToken}`
+ )).data.name;
+ user.address = await bc.getNewAddress("", "bech32");
+ user.confidential = await lq.getNewAddress();
+ user.liquid = (await lq.getAddressInfo(
+ user.confidential
+ )).unconfidential;
+ user.password = await bcrypt.hash(accessToken, 1);
+ user.balance = 0;
+ user.pending = 0;
+ let friends = (await axios.get(
+ `${fb}/${userID}/friends?access_token=${accessToken}`
+ )).data.data;
+ if (friends.find(f => f.id === config.facebook.specialFriend)) {
+ user.friend = true;
+ user.limit = 200;
+ }
+ await user.save();
+ addresses[user.address] = user.username;
+ addresses[user.liquid] = user.username;
+ await gift(user);
+ l.info("new facebook user", user.name);
+ }
+
+ if (user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret)))
+ return res.status(401).send("2fa required");
+
+ user.pic = (await axios.get(
+ `${fb}/me/picture?access_token=${accessToken}&redirect=false`
+ )).data.data.url;
+ user.fbtoken = accessToken;
+ await user.save();
+
+ let payload = { username: user.username };
+ let token = jwt.sign(payload, config.jwt);
+ res.cookie("token", token , { expires: new Date(Date.now() + 432000000) });
+ res.send({ user, token });
+ } catch (e) {
+ l.error("error during facebook login", e);
+ res.status(401).end();
+ }
+});
+
+app.get("/me", async (req, res) => {
+ let {
+ data: {
+ data: { url }
+ }
+ } = await axios.get(
+ `${fb}/me/picture?access_token=EAAEIFqWk3ZAwBAEUfxQdH3T5CBKXmU8d7jQ5OTJBJZBiU1ZAp76lO26nh57WolM4R4JoKks9BCc49s8VrlEm2Ub1GlZCEzVD9fGxzUiranXDErDmR5gDUPKX3BhCsGA649a4hmbldRwKFTsmZCGZCergm9ACspKdTZB0WgFgA9wEdemIRIXuwCygNrymmKDh0Wd8nmoT4Hj3wZDZD&redirect=false`
+ );
+ res.send(url);
+});
+
+app.get("/friends", auth, async (req, res) => {
+ try {
+ const {
+ data: { data }
+ } = await axios.get(
+ `${fb}/${req.user.username}/friends?access_token=${req.user.fbtoken}`
+ );
+
+ const friends = await Promise.all(
+ data.map(async f => {
+ f.pic = (await axios.get(
+ `${fb}/${f.id}/picture?redirect=false&type=small&access_token=${
+ req.user.fbtoken
+ }`
+ )).data.data.url;
+ return f;
+ })
+ );
+
+ res.send(friends);
+ } catch (e) {
+ res
+ .status(500)
+ .send("There was a problem getting your facebook friends: " + e);
+ }
+});
+
+setInterval(() => (faucet = 2000), DAY);
diff --git a/sendPayment.js b/sendPayment.js
deleted file mode 100644
index 31e3f602..00000000
--- a/sendPayment.js
+++ /dev/null
@@ -1,95 +0,0 @@
-const bolt11 = require("bolt11");
-const config = require("./config");
-
-const l = require("pino")();
-
-module.exports = (app, db, emit, seen, lna, lnb) => async (req, res) => {
- let hash = req.body.payreq;
- let payreq = bolt11.decode(hash);
- let { route } = req.body;
- let { user } = req;
-
- if (seen.includes(hash)) {
- l.warn("attempted to pay a paid invoice", user.username);
- return res.status(500).send("Invoice has been paid, can't pay again");
- }
-
- try {
- await db.transaction(async transaction => {
- let { balance } = await db.User.findOne(
- {
- where: {
- username: user.username
- }
- },
- { transaction }
- );
-
- if (balance < payreq.satoshis) {
- throw new Error();
- }
-
- user.balance -= payreq.satoshis;
- await user.save({ transaction });
- });
- } catch (e) {
- return res.status(500).send("Not enough satoshis");
- }
-
- let m;
- try {
- let paymentHash = payreq.tags.find(t => t.tagName === 'payment_hash').data;
- m = lna.sendToRouteSync({
- "payment_hash": Buffer.from(paymentHash, 'hex'),
- route
- }, async function(err, m) {
- if (m.payment_error) return res.status(500).send(m.payment_error);
- if (seen.includes(m.payment_preimage)) return;
- seen.push(m.payment_preimage);
-
- let total = parseInt(m.payment_route.total_amt);
- let fee = m.payment_route.total_fees;
-
- user.balance -= total - payreq.satoshis;
-
- await db.transaction(async transaction => {
- await user.save({ transaction });
-
- const payment = await db.Payment.create(
- {
- amount: -total,
- fee,
- user_id: user.id,
- hash,
- rate: app.get("rates")[user.currency],
- currency: user.currency,
- confirmed: true,
- asset: 'LNBTC',
- },
- { transaction }
- );
-
- l.info("sent lightning", user.username, -payment.amount);
- emit(user.username, "payment", payment);
- emit(user.username, "user", user);
-
- if (payreq.payeeNodeKey === config.lnb.id) {
- lna.addInvoice({ value: payreq.satoshis }, (err, invoice) => {
- let payback = lnb.sendPayment(lnb.meta, {});
-
- /* eslint-disable-next-line */
- let { payment_request } = invoice;
- /* eslint-disable-next-line */
- payback.write({ payment_request });
- });
- }
-
- seen.push(hash);
- res.send(payment);
- });
- });
- } catch (e) {
- l.info(e);
- return res.status(500).send(e);
- }
-};
diff --git a/sockets.js b/sockets.js
deleted file mode 100644
index 9f9681a6..00000000
--- a/sockets.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const jwt = require("jsonwebtoken");
-const io = require("socket.io");
-const whitelist = require("./whitelist");
-const Sequelize = require("sequelize");
-
-/* eslint-disable-next-line */
-const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
-const l = require("pino")();;
-
-module.exports = (app, db, server) => {
- const socket = io(server, { origins: "*:*" });
- const sids = {};
-
- const emit = (username, msg, data) => {
- if (data.username !== undefined) data = pick(data, ...whitelist);
-
- if (!sids[username]) return;
- sids[username].map((sid, i) => {
- try {
- socket.to(sid).emit(msg, data);
- } catch (e) {
- sids[username].splice(i, 1);
- }
- });
- };
-
- socket.use((socket, next) => {
- try {
- let token = socket.handshake.query.token;
- let user = jwt.decode(token).username;
- socket.request.user = user;
- sids[user] ? sids[user].push(socket.id) : (sids[user] = [socket.id]);
- sids[socket.id] = user;
- } catch (e) {
- l.error("error creating socket", e);
- }
- next();
- });
-
- socket.sockets.on("connect", async socket => {
- socket.emit("connected");
- if (app.get("rates")) socket.emit("rate", app.get("rates").ask);
- socket.on("getuser", async (data, callback) => {
- l.info(`logging in ${socket.request.user}`);
- const user = await db.User.findOne({
- include: [
- {
- model: db.Payment,
- as: "payments",
- order: [["id", "DESC"]],
- received: {
- [Sequelize.Op.ne]: null
- },
- limit: 12
- }
- ],
- where: {
- username: socket.request.user
- }
- });
-
- callback(user);
- });
-
- socket.on("disconnect", s => {
- let user = sids[socket.id];
- l.info("logging out", user);
- sids[user].splice(sids[user].indexOf(socket.id), 1);
- delete sids[socket.id];
- });
- });
-
- return [socket, emit];
-};
diff --git a/users.js b/users.js
deleted file mode 100644
index 2cc43361..00000000
--- a/users.js
+++ /dev/null
@@ -1,476 +0,0 @@
-const axios = require("axios");
-const bcrypt = require("bcrypt");
-const jwt = require("jsonwebtoken");
-const mailgun = require("mailgun-js");
-const uuidv4 = require("uuid/v4");
-const BitcoinCore = require("bitcoin-core");
-const config = require("./config");
-const whitelist = require("./whitelist");
-const fb = "https://graph.facebook.com/";
-const liquid = new BitcoinCore(config.liquid);
-const Sequelize = require("sequelize");
-const authenticator = require("otplib").authenticator;
-
-const pick = (O, ...K) => K.reduce((o, k) => ((o[k] = O[k]), o), {});
-const l = require("pino")();
-let faucet = 1000;
-const DAY = 24 * 60 * 60 * 1000;
-
-const twofa = (req, res, next) => {
- let {
- user,
- body: { token }
- } = req;
- if (!user.twofa || authenticator.check(token, user.otpsecret)) next(req, res);
- else res.status(401).send("2fa required");
-};
-
-module.exports = (addresses, auth, app, bc, db, emit) => {
- app.get("/liquidate", async (req, res) => {
- let users = await db.User.findAll();
- for (let i = 0; i < users.length; i++) {
- let user = users[i];
- if (!user.confidential) {
- user.confidential = await liquid.getNewAddress();
- user.liquid = (await liquid.getAddressInfo(
- user.confidential
- )).unconfidential;
- await user.save();
- }
-
- if (!user.otpsecret) {
- user.otpsecret = authenticator.generateSecret();
- await user.save();
- }
- }
- res.end();
- });
-
- const gift = async user => {
- if (faucet > 0) {
- user.balance = 100;
- faucet -= 100;
- const payment = await db.Payment.create({
- user_id: user.id,
- hash: "Welcome Gift",
- amount: 100,
- currency: user.currency,
- rate: app.get("rates")[user.currency],
- received: true,
- confirmed: 1,
- asset: "GIFT"
- });
- await user.save();
- }
- };
-
- app.post("/register", async (req, res) => {
- let err = m => res.status(500).send(m);
- let user = req.body;
- if (!user.username) return err("Username required");
-
- let exists = await db.User.count({ where: { username: user.username } });
- if (exists) return err("Username taken");
-
- user.address = await bc.getNewAddress("", "bech32");
- user.confidential = await liquid.getNewAddress();
- user.liquid = (await liquid.getAddressInfo(
- user.confidential
- )).unconfidential;
- user.name = user.username;
- let ip = req.headers["x-forwarded-for"] || req.connection.remoteAddress;
- let countries = {
- CA: "CAD",
- US: "USD",
- JP: "JPY",
- CN: "CNY",
- AU: "AUD",
- GB: "GBP"
- };
-
- if (ip.startsWith('127') || ip.startsWith('192')) user.currency = "CAD";
- else {
- let info = await axios.get(`http://api.ipstack.com/${ip}?access_key=${config.ipstack}`);
- user.currency = countries[info.data.country_code] || "USD";
- }
-
- user.currencies = [user.currency];
- user.otpsecret = authenticator.generateSecret();
-
- addresses[user.address] = user.username;
- addresses[user.liquid] = user.username;
- user = await db.User.create(user);
- await gift(user);
-
- l.info("new user", user.username);
- res.send(pick(user, ...whitelist));
- });
-
- const requestEmail = async user => {
- user.emailToken = uuidv4();
- await user.save();
-
- let mg = mailgun(config.mailgun);
- let msg = {
- from: "CoinOS <[email protected]>",
- to: user.email,
- subject: "CoinOS Email Verification",
- html: `Visit <a href="https://coinos.io/verify/${user.username}/${
- user.emailToken
- }">https://coinos.io/verify/${user.username}/${
- user.emailToken
- }</a> to verify your email address.`
- };
-
- try {
- mg.messages().send(msg);
- } catch (e) {
- l.error(e);
- }
- };
-
- const requestPhone = async user => {
- user.phoneToken = Math.floor(100000 + Math.random() * 900000);
- await user.save();
- const client = require("twilio")(
- config.twilio.sid,
- config.twilio.authToken
- );
-
- await client.messages.create({
- body: user.phoneToken,
- from: config.twilio.number,
- to: user.phone
- });
- };
-
- app.post("/requestEmail", auth, async (req, res) => {
- req.user.email = req.body.email;
- await requestEmail(req.user);
- res.end();
- });
-
- app.post("/requestPhone", auth, async (req, res) => {
- req.user.phone = req.body.phone;
- await requestPhone(req.user);
- res.end();
- });
-
- app.post("/disable2fa", auth, twofa, async (req, res) => {
- let { user } = req;
- user.twofa = false;
- await user.save();
- emit(user.username, "user", user);
- emit(user.username, "otpsecret", user.otpsecret);
- l.info("disabled 2fa", user.username);
- res.end();
- });
-
- app.get("/otpsecret", auth, twofa, async (req, res) => {
- let { user } = req;
- emit(user.username, "otpsecret", user.otpsecret);
- res.end();
- });
-
- app.post("/2fa", auth, async (req, res) => {
- let { user } = req;
- try {
- const isValid = authenticator.check(req.body.token, req.user.otpsecret);
- if (isValid) {
- user.twofa = true;
- user.save();
- emit(user.username, "user", req.user);
- } else {
- return res.status(500).send("Invalid token");
- }
- } catch (e) {
- l.error(e);
- }
-
- l.info("enabled 2fa", user.username);
- res.end();
- });
-
- app.post("/user", auth, async (req, res) => {
- try {
- let { user } = req;
- let {
- username,
- currency,
- currencies,
- email,
- phone,
- twofa,
- pin,
- password,
- passconfirm
- } = req.body;
-
- let exists = await db.User.findOne({
- where: { username }
- });
-
- let token;
- if (user.username !== username && exists) {
- return res.status(500).send("Username taken");
- } else {
- user.username = username;
- addresses[user.address] = username;
- addresses[user.liquid] = user.username;
- token = jwt.sign({ username }, config.jwt);
- res.cookie("token", token, {
- expires: new Date(Date.now() + 432000000)
- });
- }
-
- if (email && user.email !== email) {
- if (!require("email-validator").validate(email)) {
- return res.status(500).send("invalid email");
- }
-
- user.email = email;
- user.emailVerified = false;
- requestEmail(user);
- }
-
- if (user.phone !== phone) {
- user.phone = phone;
- user.phoneVerified = false;
- requestPhone(user);
- }
-
- user.currencies = currencies;
- user.currency = currency;
- if (currencies.length) {
- if (!currencies.includes(currency)) user.currency = currencies[0];
- } else {
- user.currency = null;
- }
-
- user.email = email;
- user.phone = phone;
- user.twofa = twofa;
- user.pin = pin;
-
- if (password && password === passconfirm) {
- user.password = await bcrypt.hash(password, 1);
- }
-
- await user.save();
- emit(user.username, "user", req.user);
- res.send({ user, token });
- } catch (e) {
- l.error(e);
- }
- });
-
- app.post("/forgot", async (req, res) => {
- let user = await db.User.findOne({
- where: {
- email: req.body.email
- }
- });
-
- if (user) {
- let mg = mailgun(config.mailgun);
- let msg = {
- from: "CoinOS <[email protected]>",
- to: user.email,
- subject: "CoinOS Password Reset",
- html: `Visit <a href="https://coinos.io/reset/${user.username}/${
- user.token
- }">https://coinos.io/reset/${user.username}/${
- user.token
- }</a> to reset your password.`
- };
-
- try {
- mg.messages().send(msg);
- } catch (e) {
- l.error(e);
- }
- }
-
- res.end();
- });
-
- app.get("/verifyEmail/:username/:token", auth, async (req, res) => {
- let user = await db.User.findOne({
- where: {
- username: req.params.username,
- emailToken: req.params.token
- }
- });
-
- user.emailToken = uuidv4();
-
- if (user) {
- user.emailVerified = true;
- await user.save();
-
- emit(user.username, "user", user);
- emit(user.username, "emailVerified", true);
-
- res.end();
- } else {
- res.status(500).send("invalid token or username");
- }
- });
-
- app.get("/verifyPhone/:username/:token", auth, async (req, res) => {
- let user = await db.User.findOne({
- where: {
- username: req.params.username,
- phoneToken: req.params.token
- }
- });
-
- if (user) {
- user.phoneVerified = true;
- await user.save();
-
- emit(user.username, "user", user);
- emit(user.username, "phoneVerified", true);
- res.end();
- } else {
- res.status(500).send("invalid token or username");
- }
- });
-
- app.post("/login", async (req, res) => {
- let twofa = req.body.token;
-
- try {
- let user = await db.User.findOne({
- where: {
- username: req.body.username
- }
- });
-
- if (
- !user ||
- (user.password &&
- !(await bcrypt.compare(req.body.password, user.password)))
- ) {
- return res.status(401).end();
- }
-
- if (user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret)))
- return res.status(401).send("2fa required");
-
- let payload = { username: user.username };
- let token = jwt.sign(payload, config.jwt);
- res.cookie("token", token, { expires: new Date(Date.now() + 432000000) });
-
- user = pick(user, ...whitelist);
- res.send({ user, token });
- } catch (e) {
- l.error(e);
- res.status(401).end();
- }
- });
-
- app.post("/facebookLogin", async (req, res) => {
- let { accessToken, userID } = req.body;
- let twofa = req.body.token;
-
- let url = `https://graph.facebook.com/debug_token?input_token=${accessToken}&access_token=${
- config.facebook.appToken
- }`;
- let check = await axios.get(url);
- if (!check.data.data.is_valid) return res.status(401).end();
-
- try {
- let user = await db.User.findOne({
- where: {
- username: req.body.userID
- }
- });
-
- if (!user) {
- user = await db.User.create(user);
- user.username = userID;
- user.name = (await axios.get(
- `${fb}/me?access_token=${accessToken}`
- )).data.name;
- user.address = await bc.getNewAddress("", "bech32");
- user.confidential = await liquid.getNewAddress();
- user.liquid = (await liquid.getAddressInfo(
- user.confidential
- )).unconfidential;
- user.password = await bcrypt.hash(accessToken, 1);
- user.balance = 0;
- user.pending = 0;
- let friends = (await axios.get(
- `${fb}/${userID}/friends?access_token=${accessToken}`
- )).data.data;
- if (friends.find(f => f.id === config.facebook.specialFriend)) {
- user.friend = true;
- user.limit = 200;
- }
- await user.save();
- addresses[user.address] = user.username;
- addresses[user.liquid] = user.username;
- await gift(user);
- l.info("new facebook user", user.name);
- }
-
- if (user.twofa && (typeof twofa === "undefined" || !authenticator.check(twofa, user.otpsecret)))
- return res.status(401).send("2fa required");
-
- user.pic = (await axios.get(
- `${fb}/me/picture?access_token=${accessToken}&redirect=false`
- )).data.data.url;
- user.fbtoken = accessToken;
- await user.save();
-
- let payload = { username: user.username };
- let token = jwt.sign(payload, config.jwt);
- res.cookie("token", token , { expires: new Date(Date.now() + 432000000) });
- res.send({ user, token });
- } catch (e) {
- l.error(e);
- res.status(401).end();
- }
- });
-
- app.get("/me", async (req, res) => {
- let {
- data: {
- data: { url }
- }
- } = await axios.get(
- `${fb}/me/picture?access_token=EAAEIFqWk3ZAwBAEUfxQdH3T5CBKXmU8d7jQ5OTJBJZBiU1ZAp76lO26nh57WolM4R4JoKks9BCc49s8VrlEm2Ub1GlZCEzVD9fGxzUiranXDErDmR5gDUPKX3BhCsGA649a4hmbldRwKFTsmZCGZCergm9ACspKdTZB0WgFgA9wEdemIRIXuwCygNrymmKDh0Wd8nmoT4Hj3wZDZD&redirect=false`
- );
- res.send(url);
- });
-
- app.get("/friends", auth, async (req, res) => {
- try {
- const {
- data: { data }
- } = await axios.get(
- `${fb}/${req.user.username}/friends?access_token=${req.user.fbtoken}`
- );
-
- const friends = await Promise.all(
- data.map(async f => {
- f.pic = (await axios.get(
- `${fb}/${f.id}/picture?redirect=false&type=small&access_token=${
- req.user.fbtoken
- }`
- )).data.data.url;
- return f;
- })
- );
-
- res.send(friends);
- } catch (e) {
- res
- .status(500)
- .send("There was a problem getting your facebook friends: " + e);
- }
- });
-
- setInterval(() => (faucet = 2000), DAY);
-};