From d5486393341ff999152f81c2bba09072dbe2434f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 11 Feb 2015 23:11:30 -0300 Subject: [PATCH] test passing with notifications --- lib/model/notification.js | 25 ++++++++------ lib/server.js | 68 ++++++++++++++++++++++++++++++++------- lib/storage.js | 19 +++-------- test/integration.js | 1 + 4 files changed, 79 insertions(+), 34 deletions(-) diff --git a/lib/model/notification.js b/lib/model/notification.js index 35eca65..72b5e69 100644 --- a/lib/model/notification.js +++ b/lib/model/notification.js @@ -5,21 +5,28 @@ var Uuid = require('uuid'); /* * notifications examples * - * newCopayer - * newTxProposal - * txProposalAcceptedBy - * txProposalRejectedBy - * txProposalFinallyRejected - * txProposalFinallyAccepted - * newIncommingTransaction - * newOutgoingTransaction + * NewCopayer - + * NewAddress - + * NewTxProposal - (amount) + * TxProposalAcceptedBy - (txProposalId, copayerId) + * TxProposalRejectedBy - (txProposalId, copayerId) + * txProposalFinallyRejected - txProposalId + * txProposalFinallyAccepted - txProposalId + * + * newIncommingTx (amount) + * newOutgoingTx - (txProposalId, txid) * * data Examples: * { amount: 'xxx', address: 'xxx'} * { txProposalId: 'xxx', copayerId: 'xxx' } + * + * Data is meant to provide only the needed information + * to notify the user + * */ + function Notification(opts) { opts = opts || {}; @@ -39,4 +46,4 @@ Notification.prototype.fromObj = function(obj) { return x; }; -module.export = Notification; +module.exports = Notification; diff --git a/lib/server.js b/lib/server.js index 4c4c73a..c3474e8 100644 --- a/lib/server.js +++ b/lib/server.js @@ -6,6 +6,7 @@ var log = require('npmlog'); log.debug = log.verbose; var inherits = require('inherits'); var events = require('events'); +var nodeutil = require('util'); var Bitcore = require('bitcore'); var PublicKey = Bitcore.PublicKey; @@ -22,6 +23,7 @@ var Wallet = require('./model/wallet'); var Copayer = require('./model/copayer'); var Address = require('./model/address'); var TxProposal = require('./model/txproposal'); +var Notification = require('./model/Notification'); var initialized = false; @@ -36,7 +38,7 @@ function CopayServer() { this.storage = storage; }; -util.inherits(CopayServer, events.EventEmitter); +nodeutil.inherits(CopayServer, events.EventEmitter); /** @@ -147,19 +149,22 @@ CopayServer.prototype._verifySignature = function(text, signature, pubKey) { /** - * notify + * _notify * * @param type * @param data */ -CopayServer.prototype.notify = function(type, data) { +CopayServer.prototype._notify = function(type, data) { var self = this; + var walletId = self.walletId || data.walletId; + $.checkState(walletId); + var n = new Notification({ type: type, data: data, }); - this.storage.storeNotification(this.walletId, n, function () { + this.storage.storeNotification(walletId, n, function() { self.emit(n); }); }; @@ -202,7 +207,9 @@ CopayServer.prototype.joinWallet = function(opts, cb) { wallet.addCopayer(copayer); self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) { - self.notify('newCopayer'); + self._notify('NewCopayer', { + walletId: opts.walletId, + }); return cb(err, copayer.id); }); }); @@ -227,6 +234,7 @@ CopayServer.prototype.createAddress = function(opts, cb) { self.storage.storeAddressAndWallet(wallet, address, function(err) { if (err) return cb(err); + self._notify('NewAddress'); return cb(null, address); }); }); @@ -411,17 +419,20 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) { CopayServer.prototype.createTx = function(opts, cb) { var self = this; - if (!Utils.checkRequired(opts, ['toAddress', 'amount'])) return cb(new ClientError('Required argument missing')); + if (!Utils.checkRequired(opts, ['toAddress', 'amount'])) + return cb(new ClientError('Required argument missing')); Utils.runLocked(self.walletId, cb, function(cb) { self.getWallet({}, function(err, wallet) { if (err) return cb(err); if (!wallet.isComplete()) return cb(new ClientError('Wallet is not complete')); - if (wallet.isShared() && !Utils.checkRequired(opts, 'proposalSignature')) return cb(new ClientError('Proposal signature is required for shared wallets')); + if (wallet.isShared() && !Utils.checkRequired(opts, 'proposalSignature')) + return cb(new ClientError('Proposal signature is required for shared wallets')); var copayer = wallet.getCopayer(self.copayerId); var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message; - if (!self._verifySignature(msg, opts.proposalSignature, copayer.signingPubKey)) return cb(new ClientError('Invalid proposal signature')); + if (!self._verifySignature(msg, opts.proposalSignature, copayer.signingPubKey)) + return cb(new ClientError('Invalid proposal signature')); var toAddress; try { @@ -429,7 +440,8 @@ CopayServer.prototype.createTx = function(opts, cb) { } catch (ex) { return cb(new ClientError('INVALIDADDRESS', 'Invalid address')); } - if (toAddress.network != wallet.getNetworkName()) return cb(new ClientError('INVALIDADDRESS', 'Incorrect address network')); + if (toAddress.network != wallet.getNetworkName()) + return cb(new ClientError('INVALIDADDRESS', 'Incorrect address network')); self._getUtxos(function(err, utxos) { if (err) return cb(err); @@ -463,6 +475,9 @@ CopayServer.prototype.createTx = function(opts, cb) { self.storage.storeTx(wallet.id, txp, function(err) { if (err) return cb(err); + self._notify('NewTxProposal', { + amount: opts.amount + }); return cb(null, txp); }); }); @@ -538,6 +553,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { if (actors.length == 1 && actors[0] !== self.copayerId) return cb(new ClientError('Not allowed to erase this TX')); + self._notify('transactionProposalRemoved'); self.storage.removeTx(self.walletId, opts.id, cb); }); }); @@ -590,7 +606,17 @@ CopayServer.prototype.signTx = function(opts, cb) { self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); + self._notify('TxProposalAcceptedBy', { + txProposalId: opts.txProposalId, + copayerId: self.copayerId, + }); + if (txp.status == 'accepted') { + + self._notify('TxProposalFinallyAccepted', { + txProposalId: opts.txProposalId, + }); + self._broadcastTx(txp, function(err, txid) { if (err) return cb(err); @@ -598,6 +624,11 @@ CopayServer.prototype.signTx = function(opts, cb) { self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); + self._notify('newOutgoingTx', { + txProposalId: opts.txProposalId, + txid: txid + }); + return cb(null, txp); }); }); @@ -628,14 +659,29 @@ CopayServer.prototype.rejectTx = function(opts, cb) { var action = _.find(txp.actions, { copayerId: self.copayerId }); - if (action) return cb(new ClientError('CVOTED', 'Copayer already voted on this transaction proposal')); - if (txp.status != 'pending') return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending')); + if (action) + return cb(new ClientError('CVOTED', 'Copayer already voted on this transaction proposal')); + + if (txp.status != 'pending') + return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending')); txp.reject(self.copayerId); self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); + self._notify('TxProposalRejectedBy', { + txProposalId: opts.txProposalId, + copayerId: self.copayerId, + }); + + + if (txp.status == 'rejected') { + self._notify('TxProposalFinallyRejected', { + txProposalId: opts.txProposalId, + }); + }; + return cb(); }); }); diff --git a/lib/storage.js b/lib/storage.js index de28b18..8295c45 100644 --- a/lib/storage.js +++ b/lib/storage.js @@ -311,6 +311,9 @@ Storage.prototype._removeCopayers = function(walletId, cb) { }); }; +Storage.prototype._removeAllNotifications = function(walletId, cb) { + this._delByKey(KEY.NOTIFICATION(walletId), cb); +}; Storage.prototype._removeAllAddresses = function(walletId, cb) { @@ -323,23 +326,11 @@ Storage.prototype.removeWallet = function(walletId, cb) { async.series([ function(next) { + // This should be the first step. Will check the wallet exists self._removeCopayers(walletId, next); }, function(next) { - self._removeAllAddresses(walletId, next); - }, - function(next) { - self.removeAllPendingTxs(walletId, next); - }, - function(next) { - self.removeAllTxs(walletId, next); - }, - function(next) { - var ops = [{ - type: 'del', - key: KEY.WALLET(walletId), - }]; - self.db.batch(ops, next); + self._delByKey(walletPrefix(walletId), cb); }, ], cb); }; diff --git a/test/integration.js b/test/integration.js index 1f632a3..c07897b 100644 --- a/test/integration.js +++ b/test/integration.js @@ -1202,6 +1202,7 @@ describe('Copay server', function() { server.removeWallet({}, function(err) { i = 0; server.storage._dump(function() { + server.storage._dump(); i.should.equal(0); done(); }, count);