|
|
@ -16,7 +16,7 @@ var ClientError = require('./clienterror'); |
|
|
|
var Utils = require('./utils'); |
|
|
|
var Lock = require('./lock'); |
|
|
|
var Storage = require('./storage'); |
|
|
|
var NotificationBroadcaster = require('./notificationbroadcaster'); |
|
|
|
var MessageBroker = require('./messagebroker'); |
|
|
|
var BlockchainExplorer = require('./blockchainexplorer'); |
|
|
|
|
|
|
|
var Wallet = require('./model/wallet'); |
|
|
@ -27,6 +27,7 @@ var Notification = require('./model/notification'); |
|
|
|
|
|
|
|
var initialized = false; |
|
|
|
var lock, storage, blockchainExplorer, blockchainExplorerOpts; |
|
|
|
var messageBroker; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
@ -41,13 +42,10 @@ function WalletService() { |
|
|
|
this.storage = storage; |
|
|
|
this.blockchainExplorer = blockchainExplorer; |
|
|
|
this.blockchainExplorerOpts = blockchainExplorerOpts; |
|
|
|
this.messageBroker = messageBroker; |
|
|
|
this.notifyTicker = 0; |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.onNotification = function(func) { |
|
|
|
NotificationBroadcaster.on('notification', func); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Initializes global settings for all instances. |
|
|
|
* @param {Object} opts |
|
|
@ -58,7 +56,6 @@ WalletService.onNotification = function(func) { |
|
|
|
WalletService.initialize = function(opts, cb) { |
|
|
|
$.shouldBeFunction(cb); |
|
|
|
|
|
|
|
|
|
|
|
opts = opts || {}; |
|
|
|
lock = opts.lock || new Lock(opts.lockOpts); |
|
|
|
blockchainExplorer = opts.blockchainExplorer; |
|
|
@ -67,19 +64,44 @@ WalletService.initialize = function(opts, cb) { |
|
|
|
if (initialized) |
|
|
|
return cb(); |
|
|
|
|
|
|
|
if (opts.storage) { |
|
|
|
storage = opts.storage; |
|
|
|
initialized = true; |
|
|
|
return cb(); |
|
|
|
} else { |
|
|
|
function initStorage(cb) { |
|
|
|
if (opts.storage) { |
|
|
|
storage = opts.storage; |
|
|
|
return cb(); |
|
|
|
} |
|
|
|
var newStorage = new Storage(); |
|
|
|
newStorage.connect(opts.storageOpts, function(err) { |
|
|
|
if (err) return cb(err); |
|
|
|
storage = newStorage; |
|
|
|
initialized = true; |
|
|
|
return cb(); |
|
|
|
}); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
function initMessageBroker(cb) { |
|
|
|
if (opts.messageBroker) { |
|
|
|
messageBroker = opts.messageBroker; |
|
|
|
} else { |
|
|
|
messageBroker = new MessageBroker(opts.messageBrokerOpts); |
|
|
|
} |
|
|
|
return cb(); |
|
|
|
}; |
|
|
|
|
|
|
|
async.series([ |
|
|
|
|
|
|
|
function(next) { |
|
|
|
initStorage(next); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
initMessageBroker(next); |
|
|
|
}, |
|
|
|
], function(err) { |
|
|
|
if (err) { |
|
|
|
log.error('Could not initialize', err); |
|
|
|
throw err; |
|
|
|
} |
|
|
|
initialized = true; |
|
|
|
return cb(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -260,11 +282,11 @@ WalletService.prototype.replaceTemporaryRequestKey = function(opts, cb) { |
|
|
|
walletId: opts.walletId, |
|
|
|
copayerId: self.copayerId, |
|
|
|
copayerName: opts.name, |
|
|
|
}); |
|
|
|
|
|
|
|
return cb(null, { |
|
|
|
copayerId: self.copayerId, |
|
|
|
wallet: wallet |
|
|
|
}, function() { |
|
|
|
return cb(null, { |
|
|
|
copayerId: self.copayerId, |
|
|
|
wallet: wallet |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -281,25 +303,23 @@ WalletService.prototype._verifySignature = function(text, signature, pubKey) { |
|
|
|
return WalletUtils.verifyMessage(text, signature, pubKey); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* _emit |
|
|
|
* |
|
|
|
* @param {Object} args |
|
|
|
*/ |
|
|
|
WalletService.prototype._emit = function(eventName, args) { |
|
|
|
NotificationBroadcaster.broadcast(eventName, args); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* _notify |
|
|
|
* |
|
|
|
* @param {String} type |
|
|
|
* @param {Object} data |
|
|
|
* @param {Boolean} isGlobal - If true, the notification is not issued on behalf of any particular copayer (defaults to false) |
|
|
|
* @param {Object} opts |
|
|
|
* @param {Boolean} opts.isGlobal - If true, the notification is not issued on behalf of any particular copayer (defaults to false) |
|
|
|
*/ |
|
|
|
WalletService.prototype._notify = function(type, data, isGlobal) { |
|
|
|
WalletService.prototype._notify = function(type, data, opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (_.isFunction(opts)) { |
|
|
|
cb = opts; |
|
|
|
opts = {}; |
|
|
|
} |
|
|
|
opts = opts || {}; |
|
|
|
|
|
|
|
log.debug('Notification', type, data); |
|
|
|
|
|
|
|
var walletId = self.walletId || data.walletId; |
|
|
@ -311,11 +331,12 @@ WalletService.prototype._notify = function(type, data, isGlobal) { |
|
|
|
type: type, |
|
|
|
data: data, |
|
|
|
ticker: this.notifyTicker++, |
|
|
|
creatorId: isGlobal ? null : copayerId, |
|
|
|
creatorId: opts.isGlobal ? null : copayerId, |
|
|
|
walletId: walletId, |
|
|
|
}); |
|
|
|
this.storage.storeNotification(walletId, n, function() { |
|
|
|
self._emit('notification', n); |
|
|
|
self.messageBroker.send(n); |
|
|
|
if (cb) return cb(); |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
@ -379,10 +400,11 @@ WalletService.prototype.joinWallet = function(opts, cb) { |
|
|
|
walletId: opts.walletId, |
|
|
|
copayerId: copayer.id, |
|
|
|
copayerName: copayer.name, |
|
|
|
}); |
|
|
|
return cb(null, { |
|
|
|
copayerId: copayer.id, |
|
|
|
wallet: wallet |
|
|
|
}, function() { |
|
|
|
return cb(null, { |
|
|
|
copayerId: copayer.id, |
|
|
|
wallet: wallet |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -411,8 +433,9 @@ WalletService.prototype.createAddress = function(opts, cb) { |
|
|
|
|
|
|
|
self._notify('NewAddress', { |
|
|
|
address: address.address, |
|
|
|
}, function() { |
|
|
|
return cb(null, address); |
|
|
|
}); |
|
|
|
return cb(null, address); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -710,8 +733,9 @@ WalletService.prototype.createTx = function(opts, cb) { |
|
|
|
|
|
|
|
self._notify('NewTxProposal', { |
|
|
|
amount: opts.amount |
|
|
|
}, function() { |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -783,8 +807,9 @@ WalletService.prototype.removePendingTx = function(opts, cb) { |
|
|
|
if (actors.length > 1 || (actors.length == 1 && actors[0] !== self.copayerId)) |
|
|
|
return cb(new ClientError('TXACTIONED', 'Cannot remove a proposal signed/rejected by other copayers')); |
|
|
|
|
|
|
|
self._notify('TxProposalRemoved'); |
|
|
|
self.storage.removeTx(self.walletId, txp.id, cb); |
|
|
|
self._notify('TxProposalRemoved', {}, function() { |
|
|
|
self.storage.removeTx(self.walletId, txp.id, cb); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
@ -839,18 +864,26 @@ WalletService.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, |
|
|
|
async.parallel([ |
|
|
|
|
|
|
|
function(done) { |
|
|
|
self._notify('TxProposalAcceptedBy', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
copayerId: self.copayerId, |
|
|
|
}, done); |
|
|
|
}, |
|
|
|
function(done) { |
|
|
|
if (txp.isAccepted()) { |
|
|
|
self._notify('TxProposalFinallyAccepted', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
}, done); |
|
|
|
} else { |
|
|
|
done(); |
|
|
|
} |
|
|
|
}, |
|
|
|
], function() { |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
|
|
|
|
if (txp.isAccepted()) { |
|
|
|
self._notify('TxProposalFinallyAccepted', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -892,9 +925,9 @@ WalletService.prototype.broadcastTx = function(opts, cb) { |
|
|
|
self._notify('NewOutgoingTx', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
txid: txid |
|
|
|
}, function() { |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
|
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
@ -932,19 +965,26 @@ WalletService.prototype.rejectTx = function(opts, cb) { |
|
|
|
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, |
|
|
|
}); |
|
|
|
}; |
|
|
|
async.parallel([ |
|
|
|
|
|
|
|
return cb(null, txp); |
|
|
|
function(done) { |
|
|
|
self._notify('TxProposalRejectedBy', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
copayerId: self.copayerId, |
|
|
|
}, done); |
|
|
|
}, |
|
|
|
function(done) { |
|
|
|
if (txp.status == 'rejected') { |
|
|
|
self._notify('TxProposalFinallyRejected', { |
|
|
|
txProposalId: opts.txProposalId, |
|
|
|
}, done); |
|
|
|
} else { |
|
|
|
done(); |
|
|
|
} |
|
|
|
}, |
|
|
|
], function() { |
|
|
|
return cb(null, txp); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
@ -1286,7 +1326,9 @@ WalletService.prototype.startScan = function(opts, cb) { |
|
|
|
result: err ? 'error' : 'success', |
|
|
|
}; |
|
|
|
if (err) data.error = err; |
|
|
|
self._notify('ScanFinished', data, true); |
|
|
|
self._notify('ScanFinished', data, { |
|
|
|
isGlobal: true |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
self.getWallet({}, function(err, wallet) { |
|
|
|