From 7803e4576b2cc4676bbf5203631d8c797810c13b Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Mon, 15 May 2017 16:09:52 -0300 Subject: [PATCH] tx confirmation subs API endpoints --- lib/blockchainmonitor.js | 26 +++++++- lib/model/txconfirmationsub.js | 2 + lib/server.js | 33 ++++++++++ test/integration/server.js | 111 +++++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+), 3 deletions(-) diff --git a/lib/blockchainmonitor.js b/lib/blockchainmonitor.js index e6b25ae..0f901ca 100644 --- a/lib/blockchainmonitor.js +++ b/lib/blockchainmonitor.js @@ -132,8 +132,6 @@ BlockchainMonitor.prototype._handleThirdPartyBroadcasts = function(data, process }); }; - - BlockchainMonitor.prototype._handleIncomingPayments = function(data) { var self = this; @@ -208,7 +206,7 @@ BlockchainMonitor.prototype._handleIncomingTx = function(data) { this._handleIncomingPayments(data); }; -BlockchainMonitor.prototype._handleNewBlock = function(network, hash) { +BlockchainMonitor.prototype._notifyNewBlock = function(network, hash) { var self = this; log.info('New ' + network + ' block: ', hash); @@ -228,6 +226,28 @@ BlockchainMonitor.prototype._handleNewBlock = function(network, hash) { }); }; +BlockchainMonitor.prototype._handleTxConfirmations = function(network, hash) { + var self = this; + + var explorer = self.explorers[network]; + if (!explorer) return; + + explorer.getTxidsInBlock(hash, function(err, txids) { + if (err) { + log.error('Could not fetch txids from block ' + hash); + return; + } + + // self.storage.fetchTx + + }); +}; + +BlockchainMonitor.prototype._handleNewBlock = function(network, hash) { + this._notifyNewBlock(network, hash); + this._handleTxConfirmations(network, hash); +}; + BlockchainMonitor.prototype._storeAndBroadcastNotification = function(notification, cb) { var self = this; diff --git a/lib/model/txconfirmationsub.js b/lib/model/txconfirmationsub.js index 969f390..0d01530 100644 --- a/lib/model/txconfirmationsub.js +++ b/lib/model/txconfirmationsub.js @@ -11,6 +11,7 @@ TxConfirmationSub.create = function(opts) { x.createdOn = Math.floor(Date.now() / 1000); x.copayerId = opts.copayerId; x.txid = opts.txid; + x.nbConfirmations = opts.nbConfirmations || 1; x.isActive = true; return x; }; @@ -22,6 +23,7 @@ TxConfirmationSub.fromObj = function(obj) { x.createdOn = obj.createdOn; x.copayerId = obj.copayerId; x.txid = obj.txid; + x.nbConfirmations = obj.nbConfirmations; x.isActive = obj.isActive; return x; }; diff --git a/lib/server.js b/lib/server.js index 743893b..94690c6 100644 --- a/lib/server.js +++ b/lib/server.js @@ -3136,5 +3136,38 @@ WalletService.prototype.pushNotificationsUnsubscribe = function(opts, cb) { self.storage.removePushNotificationSub(self.copayerId, opts.token, cb); }; +/** + * Subscribe this copayer to the specified tx to get a notification when the tx confirms. + * @param {Object} opts + * @param {string} opts.txid - The txid of the tx to be notified of. + * @param {string} [opts.nbConfirmations=1] - The number of confirmations to wait before sending the notification. + */ +WalletService.prototype.txConfirmationSubscribe = function(opts, cb) { + if (!checkRequired(opts, ['txid'], cb)) return; + + var self = this; + + var sub = Model.TxConfirmationSub.create({ + copayerId: self.copayerId, + txid: opts.txid, + nbConfirmations: +opts.nbConfirmations || 1, + }); + + self.storage.storeTxConfirmationSub(sub, cb); +}; + +/** + * Unsubscribe this copayer to the Push Notifications service using the specified token. + * @param {Object} opts + * @param {string} opts.txid - The txid of the tx to be notified of. + */ +WalletService.prototype.txConfirmationUnsubscribe = function(opts, cb) { + if (!checkRequired(opts, ['txid'], cb)) return; + + var self = this; + + self.storage.removeTxConfirmationSub(self.copayerId, opts.txid, cb); +}; + module.exports = WalletService; module.exports.ClientError = ClientError; diff --git a/test/integration/server.js b/test/integration/server.js index d6407a1..938220f 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -7341,4 +7341,115 @@ describe('Wallet service', function() { }); }); }); + + describe('Tx confirmation notifications', function() { + var server, wallet; + beforeEach(function(done) { + helpers.createAndJoinWallet(2, 3, function(s, w) { + server = s; + wallet = w; + done(); + }); + }); + + it('should subscribe copayer to a tx confirmation', function(done) { + helpers.getAuthServer(wallet.copayers[0].id, function(server) { + should.exist(server); + server.txConfirmationSubscribe({ + txid: '123', + }, function(err) { + should.not.exist(err); + server.storage.fetchActiveTxConfirmationSubs(wallet.copayers[0].id, function(err, subs) { + should.not.exist(err); + should.exist(subs); + subs.length.should.equal(1); + var s = subs[0]; + s.txid.should.equal('123'); + s.nbConfirmations.should.equal(1); + s.isActive.should.be.true; + done(); + }); + }); + }); + }); + it('should overwrite last subscription', function(done) { + helpers.getAuthServer(wallet.copayers[0].id, function(server) { + should.exist(server); + server.txConfirmationSubscribe({ + txid: '123', + }, function(err) { + server.txConfirmationSubscribe({ + txid: '123', + nbConfirmations: 6, + }, function(err) { + should.not.exist(err); + server.storage.fetchActiveTxConfirmationSubs(wallet.copayers[0].id, function(err, subs) { + should.not.exist(err); + should.exist(subs); + subs.length.should.equal(1); + var s = subs[0]; + s.txid.should.equal('123'); + s.nbConfirmations.should.equal(6); + s.isActive.should.be.true; + done(); + }); + }); + }); + }); + }); + + it('should unsubscribe copayer to the specified tx', function(done) { + helpers.getAuthServer(wallet.copayers[0].id, function(server) { + should.exist(server); + async.series([ + + function(next) { + server.txConfirmationSubscribe({ + txid: '123', + }, next); + }, + function(next) { + server.txConfirmationSubscribe({ + txid: '456', + }, next); + }, + function(next) { + server.txConfirmationUnsubscribe({ + txid: '123', + }, next); + }, + function(next) { + server.storage.fetchActiveTxConfirmationSubs(wallet.copayers[0].id, function(err, subs) { + should.not.exist(err); + should.exist(subs); + subs.length.should.equal(1); + var s = subs[0]; + s.txid.should.equal('456'); + next(); + }); + }, + function(next) { + helpers.getAuthServer(wallet.copayers[1].id, function(server) { + server.txConfirmationUnsubscribe({ + txid: '456' + }, next); + }); + }, + function(next) { + server.storage.fetchActiveTxConfirmationSubs(wallet.copayers[0].id, function(err, subs) { + should.not.exist(err); + should.exist(subs); + subs.length.should.equal(1); + var s = subs[0]; + s.txid.should.equal('456'); + next(); + }); + }, + ], function(err) { + should.not.exist(err); + done(); + }); + }); + }); + }); });