UNPKG

@ducatus/ducatus-wallet-service-rev

Version:
1,264 lines 44.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var async = __importStar(require("async")); var lodash_1 = __importDefault(require("lodash")); var mongodb = __importStar(require("mongodb")); var model_1 = require("./model"); var BCHAddressTranslator = require('./bchaddresstranslator'); var $ = require('preconditions').singleton(); var log = require('npmlog'); log.debug = log.verbose; log.disableColor(); var collections = { WALLETS: 'wallets', TXS: 'txs', ADDRESSES: 'addresses', NOTIFICATIONS: 'notifications', COPAYERS_LOOKUP: 'copayers_lookup', PREFERENCES: 'preferences', EMAIL_QUEUE: 'email_queue', CACHE: 'cache', FIAT_RATES2: 'fiat_rates2', TX_NOTES: 'tx_notes', SESSIONS: 'sessions', PUSH_NOTIFICATION_SUBS: 'push_notification_subs', TX_CONFIRMATION_SUBS: 'tx_confirmation_subs', LOCKS: 'locks' }; var Storage = (function () { function Storage(opts) { var _this = this; if (opts === void 0) { opts = {}; } this.walletCheck = function (params) { return __awaiter(_this, void 0, void 0, function () { var walletId; var _this = this; return __generator(this, function (_a) { walletId = params.walletId; return [2, new Promise(function (resolve) { var addressStream = _this.db.collection(collections.ADDRESSES).find({ walletId: walletId }); var sum = 0; var lastAddress; addressStream.on('data', function (walletAddress) { if (walletAddress.address) { lastAddress = walletAddress.address.replace(/:.*$/, ''); var addressSum = Buffer.from(lastAddress).reduce(function (tot, cur) { return (tot + cur) % Number.MAX_SAFE_INTEGER; }); sum = (sum + addressSum) % Number.MAX_SAFE_INTEGER; } }); addressStream.on('end', function () { resolve({ lastAddress: lastAddress, sum: sum }); }); })]; }); }); }; opts = opts || {}; this.db = opts.db; } Storage.createIndexes = function (db) { log.info('Creating DB indexes'); db.collection(collections.WALLETS).createIndex({ id: 1 }); db.collection(collections.COPAYERS_LOOKUP).createIndex({ copayerId: 1 }); db.collection(collections.COPAYERS_LOOKUP).createIndex({ walletId: 1 }); db.collection(collections.TXS).createIndex({ walletId: 1, id: 1 }); db.collection(collections.TXS).createIndex({ walletId: 1, isPending: 1, txid: 1 }); db.collection(collections.TXS).createIndex({ walletId: 1, createdOn: -1 }); db.collection(collections.TXS).createIndex({ txid: 1 }); db.collection(collections.NOTIFICATIONS).createIndex({ walletId: 1, id: 1 }); db.collection(collections.ADDRESSES).createIndex({ walletId: 1, createdOn: 1 }); db.collection(collections.ADDRESSES).createIndex({ address: 1 }, { unique: true }); db.collection(collections.ADDRESSES).createIndex({ address: 1, beRegistered: 1 }); db.collection(collections.ADDRESSES).createIndex({ walletId: 1, address: 1 }); db.collection(collections.EMAIL_QUEUE).createIndex({ id: 1 }); db.collection(collections.EMAIL_QUEUE).createIndex({ notificationId: 1 }); db.collection(collections.CACHE).createIndex({ walletId: 1, type: 1, key: 1 }); db.collection(collections.TX_NOTES).createIndex({ walletId: 1, txid: 1 }); db.collection(collections.PREFERENCES).createIndex({ walletId: 1 }); db.collection(collections.FIAT_RATES2).createIndex({ coin: 1, code: 1, ts: 1 }); db.collection(collections.PUSH_NOTIFICATION_SUBS).createIndex({ copayerId: 1 }); db.collection(collections.TX_CONFIRMATION_SUBS).createIndex({ copayerId: 1, txid: 1 }); db.collection(collections.TX_CONFIRMATION_SUBS).createIndex({ isActive: 1, copayerId: 1 }); db.collection(collections.SESSIONS).createIndex({ copayerId: 1 }); }; Storage.prototype.connect = function (opts, cb) { var _this = this; opts = opts || {}; if (this.db) return cb(); var config = opts.mongoDb || {}; if (opts.secondaryPreferred) { if (config.uri.indexOf('?') > 0) { config.uri = config.uri + '&'; } else { config.uri = config.uri + '?'; } config.uri = config.uri + 'readPreference=secondaryPreferred'; log.info('Read operations set to secondaryPreferred'); } mongodb.MongoClient.connect(config.uri, function (err, db) { if (err) { log.error('Unable to connect to the mongoDB. Check the credentials.'); return cb(err); } _this.db = db; log.info('Connection established to mongoDB:' + config.uri); Storage.createIndexes(db); return cb(); }); }; Storage.prototype.disconnect = function (cb) { var _this = this; this.db.close(true, function (err) { if (err) return cb(err); _this.db = null; return cb(); }); }; Storage.prototype.fetchWallet = function (id, cb) { if (!this.db) return cb('not ready'); this.db.collection(collections.WALLETS).findOne({ id: id }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, model_1.Wallet.fromObj(result)); }); }; Storage.prototype.storeWallet = function (wallet, cb) { this.db.collection(collections.WALLETS).update({ id: wallet.id }, wallet.toObject(), { w: 1, upsert: true }, cb); }; Storage.prototype.storeWalletAndUpdateCopayersLookup = function (wallet, cb) { var _this = this; var copayerLookups = lodash_1.default.map(wallet.copayers, function (copayer) { try { $.checkState(copayer.requestPubKeys); } catch (e) { return cb(e); } return { copayerId: copayer.id, walletId: wallet.id, requestPubKeys: copayer.requestPubKeys }; }); this.db.collection(collections.COPAYERS_LOOKUP).remove({ walletId: wallet.id }, { w: 1 }, function (err) { if (err) return cb(err); _this.db.collection(collections.COPAYERS_LOOKUP).insert(copayerLookups, { w: 1 }, function (err) { if (err) return cb(err); return _this.storeWallet(wallet, cb); }); }); }; Storage.prototype.fetchCopayerLookup = function (copayerId, cb) { this.db.collection(collections.COPAYERS_LOOKUP).findOne({ copayerId: copayerId }, function (err, result) { if (err) return cb(err); if (!result) return cb(); if (!result.requestPubKeys) { result.requestPubKeys = [ { key: result.requestPubKey, signature: result.signature } ]; } return cb(null, result); }); }; Storage.prototype._completeTxData = function (walletId, txs, cb) { this.fetchWallet(walletId, function (err, wallet) { if (err) return cb(err); lodash_1.default.each([].concat(txs), function (tx) { tx.derivationStrategy = wallet.derivationStrategy || 'BIP45'; tx.creatorName = wallet.getCopayer(tx.creatorId).name; lodash_1.default.each(tx.actions, function (action) { action.copayerName = wallet.getCopayer(action.copayerId).name; }); if (tx.status == 'accepted') tx.raw = tx.getRawTx(); }); return cb(null, txs); }); }; Storage.prototype.fetchTx = function (walletId, txProposalId, cb) { var _this = this; if (!this.db) return cb(); this.db.collection(collections.TXS).findOne({ id: txProposalId, walletId: walletId }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return _this._completeTxData(walletId, model_1.TxProposal.fromObj(result), cb); }); }; Storage.prototype.fetchTxByHash = function (hash, cb) { var _this = this; if (!this.db) return cb(); this.db.collection(collections.TXS).findOne({ txid: hash }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return _this._completeTxData(result.walletId, model_1.TxProposal.fromObj(result), cb); }); }; Storage.prototype.fetchLastTxs = function (walletId, creatorId, limit, cb) { this.db .collection(collections.TXS) .find({ walletId: walletId, creatorId: creatorId }, { limit: limit || 5 }) .sort({ createdOn: -1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var txs = lodash_1.default.map(result, function (tx) { return model_1.TxProposal.fromObj(tx); }); return cb(null, txs); }); }; Storage.prototype.fetchPendingTxs = function (walletId, cb) { var _this = this; this.db .collection(collections.TXS) .find({ walletId: walletId, isPending: true }) .sort({ createdOn: -1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var txs = lodash_1.default.map(result, function (tx) { return model_1.TxProposal.fromObj(tx); }); return _this._completeTxData(walletId, txs, cb); }); }; Storage.prototype.fetchTxs = function (walletId, opts, cb) { var _this = this; opts = opts || {}; var tsFilter = {}; if (lodash_1.default.isNumber(opts.minTs)) tsFilter.$gte = opts.minTs; if (lodash_1.default.isNumber(opts.maxTs)) tsFilter.$lte = opts.maxTs; var filter = { walletId: walletId }; if (!lodash_1.default.isEmpty(tsFilter)) filter.createdOn = tsFilter; var mods = {}; if (lodash_1.default.isNumber(opts.limit)) mods.limit = opts.limit; this.db .collection(collections.TXS) .find(filter, mods) .sort({ createdOn: -1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var txs = lodash_1.default.map(result, function (tx) { return model_1.TxProposal.fromObj(tx); }); return _this._completeTxData(walletId, txs, cb); }); }; Storage.prototype.fetchBroadcastedTxs = function (walletId, opts, cb) { var _this = this; opts = opts || {}; var tsFilter = {}; if (lodash_1.default.isNumber(opts.minTs)) tsFilter.$gte = opts.minTs; if (lodash_1.default.isNumber(opts.maxTs)) tsFilter.$lte = opts.maxTs; var filter = { walletId: walletId, status: 'broadcasted' }; if (!lodash_1.default.isEmpty(tsFilter)) filter.broadcastedOn = tsFilter; var mods = {}; if (lodash_1.default.isNumber(opts.limit)) mods.limit = opts.limit; this.db .collection(collections.TXS) .find(filter, mods) .sort({ createdOn: -1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var txs = lodash_1.default.map(result, function (tx) { return model_1.TxProposal.fromObj(tx); }); return _this._completeTxData(walletId, txs, cb); }); }; Storage.prototype.fetchNotifications = function (walletId, notificationId, minTs, cb) { function makeId(timestamp) { return lodash_1.default.padStart(timestamp, 14, '0') + lodash_1.default.repeat('0', 4); } var minId = makeId(minTs); if (notificationId) { minId = notificationId > minId ? notificationId : minId; } this.db .collection(collections.NOTIFICATIONS) .find({ walletId: walletId, id: { $gt: minId } }) .sort({ id: 1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var notifications = lodash_1.default.map(result, function (notification) { return model_1.Notification.fromObj(notification); }); return cb(null, notifications); }); }; Storage.prototype.storeNotification = function (walletId, notification, cb) { if (!this.db) { log.warn('Trying to store a notification with close DB', notification); return; } this.db.collection(collections.NOTIFICATIONS).insert(notification, { w: 1 }, cb); }; Storage.prototype.storeTx = function (walletId, txp, cb) { this.db.collection(collections.TXS).update({ id: txp.id, walletId: walletId }, txp.toObject(), { w: 1, upsert: true }, cb); }; Storage.prototype.removeTx = function (walletId, txProposalId, cb) { this.db.collection(collections.TXS).remove({ id: txProposalId, walletId: walletId }, { w: 1 }, cb); }; Storage.prototype.removeWallet = function (walletId, cb) { var _this = this; async.parallel([ function (next) { _this.db.collection(collections.WALLETS).remove({ id: walletId }, next); }, function (next) { var otherCollections = lodash_1.default.without(lodash_1.default.values(collections), collections.WALLETS); async.each(otherCollections, function (col, next) { _this.db.collection(col).remove({ walletId: walletId }, next); }, next); } ], cb); }; Storage.prototype.fetchAddresses = function (walletId, cb) { this.db .collection(collections.ADDRESSES) .find({ walletId: walletId }) .sort({ createdOn: 1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, result.map(model_1.Address.fromObj)); }); }; Storage.prototype.fetchAddress = function (address, cb) { this.db .collection(collections.ADDRESSES) .find({ address: address }) .sort({ createdOn: 1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, result.map(model_1.Address.fromObj)); }); }; Storage.prototype.migrateToCashAddr = function (walletId, cb) { var _this = this; var cursor = this.db.collection(collections.ADDRESSES).find({ walletId: walletId }); cursor.on('end', function () { console.log("Migration to cash address of " + walletId + " Finished"); return _this.clearWalletCache(walletId, cb); }); cursor.on('err', function (err) { return cb(err); }); cursor.on('data', function (doc) { cursor.pause(); var x; try { x = BCHAddressTranslator.translate(doc.address, 'cashaddr'); } catch (e) { return cb(e); } _this.db.collection(collections.ADDRESSES).update({ _id: doc._id }, { $set: { address: x } }, { multi: true }); cursor.resume(); }); }; Storage.prototype.fetchUnsyncAddresses = function (walletId, cb) { this.db .collection(collections.ADDRESSES) .find({ walletId: walletId, beRegistered: null }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, result); }); }; Storage.prototype.fetchNewAddresses = function (walletId, fromTs, cb) { this.db .collection(collections.ADDRESSES) .find({ walletId: walletId, createdOn: { $gte: fromTs } }) .sort({ createdOn: 1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, result.map(model_1.Address.fromObj)); }); }; Storage.prototype.storeAddress = function (address, cb) { this.db.collection(collections.ADDRESSES).update({ walletId: address.walletId, address: address.address }, address, { w: 1, upsert: false }, cb); }; Storage.prototype.markSyncedAddresses = function (addresses, cb) { this.db.collection(collections.ADDRESSES).updateMany({ address: { $in: addresses } }, { $set: { beRegistered: true } }, { w: 1, upsert: false }, cb); }; Storage.prototype.deregisterWallet = function (walletId, cb) { var _this = this; this.db.collection(collections.WALLETS).update({ id: walletId }, { $set: { beRegistered: null } }, { w: 1, upsert: false }, function () { _this.db.collection(collections.ADDRESSES).update({ walletId: walletId }, { $set: { beRegistered: null } }, { w: 1, upsert: false, multi: true }, function () { _this.clearWalletCache(walletId, cb); }); }); }; Storage.prototype.storeAddressAndWallet = function (wallet, addresses, cb) { var _this = this; var clonedAddresses = [].concat(addresses); if (lodash_1.default.isEmpty(addresses)) return cb(); var duplicate; this.db.collection(collections.ADDRESSES).insert(clonedAddresses, { w: 1 }, function (err) { if (err) { if (!err.toString().match(/E11000/)) { return cb(err); } else { duplicate = true; log.warn('Found duplicate address: ' + lodash_1.default.join(lodash_1.default.map(clonedAddresses, 'address'), ',')); } } _this.storeWallet(wallet, function (err) { return cb(err, duplicate); }); }); }; Storage.prototype.fetchAddressByWalletId = function (walletId, address, cb) { this.db.collection(collections.ADDRESSES).findOne({ walletId: walletId, address: address }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, model_1.Address.fromObj(result)); }); }; Storage.prototype.fetchAddressesByWalletId = function (walletId, addresses, cb) { this.db .collection(collections.ADDRESSES) .find({ walletId: walletId, address: { $in: addresses } }, {}) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, result); }); }; Storage.prototype.fetchAddressByCoin = function (coin, address, cb) { if (!this.db) return cb(); this.db .collection(collections.ADDRESSES) .find({ address: address }) .toArray(function (err, result) { if (err) return cb(err); if (!result || lodash_1.default.isEmpty(result)) return cb(); if (result.length > 1) { result = lodash_1.default.find(result, function (address) { return coin == (address.coin || 'btc'); }); } else { result = lodash_1.default.head(result); } if (!result) return cb(); return cb(null, model_1.Address.fromObj(result)); }); }; Storage.prototype.fetchPreferences = function (walletId, copayerId, cb) { this.db .collection(collections.PREFERENCES) .find({ walletId: walletId }) .toArray(function (err, result) { if (err) return cb(err); if (copayerId) { result = lodash_1.default.find(result, { copayerId: copayerId }); } if (!result) return cb(); var preferences = lodash_1.default.map([].concat(result), function (r) { return model_1.Preferences.fromObj(r); }); if (copayerId) { return cb(null, preferences[0]); } else { return cb(null, preferences); } }); }; Storage.prototype.storePreferences = function (preferences, cb) { this.db.collection(collections.PREFERENCES).update({ walletId: preferences.walletId, copayerId: preferences.copayerId }, preferences, { w: 1, upsert: true }, cb); }; Storage.prototype.storeEmail = function (email, cb) { this.db.collection(collections.EMAIL_QUEUE).update({ id: email.id }, email, { w: 1, upsert: true }, cb); }; Storage.prototype.fetchUnsentEmails = function (cb) { this.db .collection(collections.EMAIL_QUEUE) .find({ status: 'fail' }) .toArray(function (err, result) { if (err) return cb(err); if (!result || lodash_1.default.isEmpty(result)) return cb(null, []); var emails = lodash_1.default.map(result, function (x) { return model_1.Email.fromObj(x); }); return cb(null, emails); }); }; Storage.prototype.fetchEmailByNotification = function (notificationId, cb) { this.db.collection(collections.EMAIL_QUEUE).findOne({ notificationId: notificationId }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return cb(null, model_1.Email.fromObj(result)); }); }; Storage.prototype.getTxHistoryCacheStatusV8 = function (walletId, cb) { this.db.collection(collections.CACHE).findOne({ walletId: walletId, type: 'historyCacheStatusV8', key: null }, function (err, result) { if (err) return cb(err); if (!result) return cb(null, { tipId: null, tipIndex: null }); return cb(null, { updatedOn: result.updatedOn, updatedHeight: result.updatedHeight, tipIndex: result.tipIndex, tipTxId: result.tipTxId, tipHeight: result.tipHeight }); }); }; Storage.prototype.getWalletAddressChecked = function (walletId, cb) { this.db.collection(collections.CACHE).findOne({ walletId: walletId, type: 'addressChecked', key: null }, function (err, result) { if (err || !result) return cb(err); return cb(null, result.totalAddresses); }); }; Storage.prototype.setWalletAddressChecked = function (walletId, totalAddresses, cb) { this.db.collection(collections.CACHE).update({ walletId: walletId, type: 'addressChecked', key: null }, { walletId: walletId, type: 'addressChecked', key: null, totalAddresses: totalAddresses }, { w: 1, upsert: true }, cb); }; Storage.prototype.getTxHistoryCacheV8 = function (walletId, skip, limit, cb) { var _this = this; $.checkArgument(skip >= 0); $.checkArgument(limit >= 0); this.getTxHistoryCacheStatusV8(walletId, function (err, cacheStatus) { if (err) return cb(err); if (lodash_1.default.isNull(cacheStatus.tipId)) return cb(null, []); var firstPosition = cacheStatus.tipIndex - skip - limit + 1; var lastPosition = cacheStatus.tipIndex - skip + 1; if (firstPosition < 0) firstPosition = 0; if (lastPosition <= 0) return cb(null, []); _this.db .collection(collections.CACHE) .find({ walletId: walletId, type: 'historyCacheV8', key: { $gte: firstPosition, $lt: lastPosition } }) .sort({ key: -1 }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var txs = lodash_1.default.map(result, 'tx'); return cb(null, txs); }); }); }; Storage.prototype.clearWalletCache = function (walletId, cb) { this.db.collection(collections.CACHE).deleteMany({ walletId: walletId }, {}, cb); }; Storage.prototype.storeTxHistoryStreamV8 = function (walletId, streamKey, items, cb) { this.db.collection(collections.CACHE).update({ walletId: walletId, type: 'historyStream', key: null }, { walletId: walletId, type: 'historyStream', key: null, streamKey: streamKey, items: items }, { w: 1, upsert: true }, cb); }; Storage.prototype.clearTxHistoryStreamV8 = function (walletId, cb) { this.db.collection(collections.CACHE).deleteMany({ walletId: walletId, type: 'historyStream', key: null }, {}, cb); }; Storage.prototype.getTxHistoryStreamV8 = function (walletId, cb) { this.db.collection(collections.CACHE).findOne({ walletId: walletId, type: 'historyStream', key: null }, function (err, result) { if (err || !result) return cb(err); return cb(null, result); }); }; Storage.prototype.storeTxHistoryCacheV8 = function (walletId, tipIndex, items, updateHeight, cb) { var _this = this; var index = lodash_1.default.isNull(tipIndex) ? 0 : tipIndex + 1; var pos; lodash_1.default.each(items.reverse(), function (item) { item.position = index++; }); async.each(items, function (item, next) { pos = item.position; delete item.position; _this.db.collection(collections.CACHE).insert({ walletId: walletId, type: 'historyCacheV8', key: pos, tx: item }, next); }, function (err) { if (err) return cb(err); var first = lodash_1.default.first(items); var last = lodash_1.default.last(items); try { $.checkState(last.txid, 'missing txid in tx to be cached'); $.checkState(last.blockheight, 'missing blockheight in tx to be cached'); $.checkState(first.blockheight, 'missing blockheight in tx to be cached'); $.checkState(last.blockheight >= 0, 'blockheight <=0 om tx to be cached'); $.checkState(first.blockheight <= last.blockheight, 'tx to be cached are in wrong order (lastest should be first)'); } catch (e) { return cb(e); } log.debug("Cache Last Item: " + last.txid + " blockh: " + last.blockheight + " updatedh: " + updateHeight); _this.db.collection(collections.CACHE).update({ walletId: walletId, type: 'historyCacheStatusV8', key: null }, { walletId: walletId, type: 'historyCacheStatusV8', key: null, updatedOn: Date.now(), updatedHeight: updateHeight, tipIndex: pos, tipTxId: last.txid, tipHeight: last.blockheight }, { w: 1, upsert: true }, cb); }); }; Storage.prototype.storeFiatRate = function (coin, rates, cb) { var _this = this; var now = Date.now(); async.each(rates, function (rate, next) { var i = { ts: now, coin: coin, code: rate.code, value: rate.value }; _this.db.collection(collections.FIAT_RATES2).insert(i, { w: 1 }, next); }, cb); }; Storage.prototype.fetchFiatRate = function (coin, code, ts, cb) { this.db .collection(collections.FIAT_RATES2) .find({ coin: coin, code: code, ts: { $lte: ts } }) .sort({ ts: -1 }) .limit(1) .toArray(function (err, result) { if (err || lodash_1.default.isEmpty(result)) return cb(err); return cb(null, result[0]); }); }; Storage.prototype.fetchHistoricalRates = function (coin, code, ts, cb) { this.db .collection(collections.FIAT_RATES2) .find({ coin: coin, code: code, ts: { $gte: ts } }) .sort({ ts: -1 }) .toArray(function (err, result) { if (err || lodash_1.default.isEmpty(result)) return cb(err); return cb(null, result); }); }; Storage.prototype.fetchTxNote = function (walletId, txid, cb) { var _this = this; this.db.collection(collections.TX_NOTES).findOne({ walletId: walletId, txid: txid }, function (err, result) { if (err) return cb(err); if (!result) return cb(); return _this._completeTxNotesData(walletId, model_1.TxNote.fromObj(result), cb); }); }; Storage.prototype._completeTxNotesData = function (walletId, notes, cb) { this.fetchWallet(walletId, function (err, wallet) { if (err) return cb(err); lodash_1.default.each([].concat(notes), function (note) { note.editedByName = wallet.getCopayer(note.editedBy).name; }); return cb(null, notes); }); }; Storage.prototype.fetchTxNotes = function (walletId, opts, cb) { var _this = this; var filter = { walletId: walletId }; if (lodash_1.default.isNumber(opts.minTs)) filter.editedOn = { $gte: opts.minTs }; this.db .collection(collections.TX_NOTES) .find(filter) .toArray(function (err, result) { if (err) return cb(err); var notes = lodash_1.default.compact(lodash_1.default.map(result, function (note) { return model_1.TxNote.fromObj(note); })); return _this._completeTxNotesData(walletId, notes, cb); }); }; Storage.prototype.storeTxNote = function (txNote, cb) { this.db.collection(collections.TX_NOTES).update({ txid: txNote.txid, walletId: txNote.walletId }, txNote.toObject(), { w: 1, upsert: true }, cb); }; Storage.prototype.getSession = function (copayerId, cb) { this.db.collection(collections.SESSIONS).findOne({ copayerId: copayerId }, function (err, result) { if (err || !result) return cb(err); return cb(null, model_1.Session.fromObj(result)); }); }; Storage.prototype.storeSession = function (session, cb) { this.db.collection(collections.SESSIONS).update({ copayerId: session.copayerId }, session.toObject(), { w: 1, upsert: true }, cb); }; Storage.prototype.fetchPushNotificationSubs = function (copayerId, cb) { this.db .collection(collections.PUSH_NOTIFICATION_SUBS) .find({ copayerId: copayerId }) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var tokens = lodash_1.default.map([].concat(result), function (r) { return model_1.PushNotificationSub.fromObj(r); }); return cb(null, tokens); }); }; Storage.prototype.storePushNotificationSub = function (pushNotificationSub, cb) { this.db.collection(collections.PUSH_NOTIFICATION_SUBS).update({ copayerId: pushNotificationSub.copayerId, token: pushNotificationSub.token }, pushNotificationSub, { w: 1, upsert: true }, cb); }; Storage.prototype.removePushNotificationSub = function (copayerId, token, cb) { this.db.collection(collections.PUSH_NOTIFICATION_SUBS).remove({ copayerId: copayerId, token: token }, { w: 1 }, cb); }; Storage.prototype.fetchActiveTxConfirmationSubs = function (copayerId, cb) { if (!this.db) { log.warn('Trying to fetch notifications with closed DB'); return; } var filter = { isActive: true }; if (copayerId) filter.copayerId = copayerId; this.db .collection(collections.TX_CONFIRMATION_SUBS) .find(filter) .toArray(function (err, result) { if (err) return cb(err); if (!result) return cb(); var subs = lodash_1.default.map([].concat(result), function (r) { return model_1.TxConfirmationSub.fromObj(r); }); return cb(null, subs); }); }; Storage.prototype.storeTxConfirmationSub = function (txConfirmationSub, cb) { this.db.collection(collections.TX_CONFIRMATION_SUBS).update({ copayerId: txConfirmationSub.copayerId, txid: txConfirmationSub.txid }, txConfirmationSub, { w: 1, upsert: true }, cb); }; Storage.prototype.removeTxConfirmationSub = function (copayerId, txid, cb) { this.db.collection(collections.TX_CONFIRMATION_SUBS).remove({ copayerId: copayerId, txid: txid }, { w: 1 }, cb); }; Storage.prototype._dump = function (cb, fn) { fn = fn || console.log; cb = cb || function () { }; this.db.collections(function (err, collections) { if (err) return cb(err); async.eachSeries(collections, function (col, next) { col.find().toArray(function (err, items) { fn('--------', col.s.name); fn(items); fn('------------------------------------------------------------------\n\n'); next(err); }); }, cb); }); }; Storage.prototype.checkAndUseGlobalCache = function (key, duration, cb) { var now = Date.now(); this.db.collection(collections.CACHE).findOne({ key: key, walletId: null, type: null }, function (err, ret) { if (err) return cb(err); if (!ret) return cb(); var validFor = ret.ts + duration - now; return cb(null, validFor > 0 ? ret.result : null, ret.result); }); }; Storage.prototype.storeGlobalCache = function (key, values, cb) { var now = Date.now(); this.db.collection(collections.CACHE).update({ key: key, walletId: null, type: null }, { $set: { ts: now, result: values } }, { w: 1, upsert: true }, cb); }; Storage.prototype.clearGlobalCache = function (key, cb) { this.db.collection(collections.CACHE).remove({ key: key, walletId: null, type: null }, { w: 1 }, cb); }; Storage.prototype.acquireLock = function (key, expireTs, cb) { this.db.collection(collections.LOCKS).insert({ _id: key, expireOn: expireTs }, {}, cb); }; Storage.prototype.releaseLock = function (key, cb) { this.db.collection(collections.LOCKS).remove({ _id: key }, {}, cb); }; Storage.prototype.clearExpiredLock = function (key, cb) { var _this = this; this.db.collection(collections.LOCKS).findOne({ _id: key }, function (err, ret) { if (err || !ret) return; if (ret.expireOn < Date.now()) { log.info('Releasing expired lock : ' + key); return _this.releaseLock(key, cb); } return cb(); }); }; Storage.BCHEIGHT_KEY = 'bcheight'; Storage.collections = collections; return Storage; }()); exports.Storage = Storage; //# sourceMappingURL=storage.js.map