test passing with notifications
This commit is contained in:
parent
7a4d16f88e
commit
d548639334
|
@ -5,21 +5,28 @@ var Uuid = require('uuid');
|
||||||
/*
|
/*
|
||||||
* notifications examples
|
* notifications examples
|
||||||
*
|
*
|
||||||
* newCopayer
|
* NewCopayer -
|
||||||
* newTxProposal
|
* NewAddress -
|
||||||
* txProposalAcceptedBy
|
* NewTxProposal - (amount)
|
||||||
* txProposalRejectedBy
|
* TxProposalAcceptedBy - (txProposalId, copayerId)
|
||||||
* txProposalFinallyRejected
|
* TxProposalRejectedBy - (txProposalId, copayerId)
|
||||||
* txProposalFinallyAccepted
|
* txProposalFinallyRejected - txProposalId
|
||||||
* newIncommingTransaction
|
* txProposalFinallyAccepted - txProposalId
|
||||||
* newOutgoingTransaction
|
*
|
||||||
|
* newIncommingTx (amount)
|
||||||
|
* newOutgoingTx - (txProposalId, txid)
|
||||||
*
|
*
|
||||||
* data Examples:
|
* data Examples:
|
||||||
* { amount: 'xxx', address: 'xxx'}
|
* { amount: 'xxx', address: 'xxx'}
|
||||||
* { txProposalId: 'xxx', copayerId: 'xxx' }
|
* { txProposalId: 'xxx', copayerId: 'xxx' }
|
||||||
|
*
|
||||||
|
* Data is meant to provide only the needed information
|
||||||
|
* to notify the user
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function Notification(opts) {
|
function Notification(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
|
@ -39,4 +46,4 @@ Notification.prototype.fromObj = function(obj) {
|
||||||
return x;
|
return x;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.export = Notification;
|
module.exports = Notification;
|
||||||
|
|
|
@ -6,6 +6,7 @@ var log = require('npmlog');
|
||||||
log.debug = log.verbose;
|
log.debug = log.verbose;
|
||||||
var inherits = require('inherits');
|
var inherits = require('inherits');
|
||||||
var events = require('events');
|
var events = require('events');
|
||||||
|
var nodeutil = require('util');
|
||||||
|
|
||||||
var Bitcore = require('bitcore');
|
var Bitcore = require('bitcore');
|
||||||
var PublicKey = Bitcore.PublicKey;
|
var PublicKey = Bitcore.PublicKey;
|
||||||
|
@ -22,6 +23,7 @@ var Wallet = require('./model/wallet');
|
||||||
var Copayer = require('./model/copayer');
|
var Copayer = require('./model/copayer');
|
||||||
var Address = require('./model/address');
|
var Address = require('./model/address');
|
||||||
var TxProposal = require('./model/txproposal');
|
var TxProposal = require('./model/txproposal');
|
||||||
|
var Notification = require('./model/Notification');
|
||||||
|
|
||||||
|
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
|
@ -36,7 +38,7 @@ function CopayServer() {
|
||||||
this.storage = storage;
|
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 type
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
CopayServer.prototype.notify = function(type, data) {
|
CopayServer.prototype._notify = function(type, data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
var walletId = self.walletId || data.walletId;
|
||||||
|
$.checkState(walletId);
|
||||||
|
|
||||||
var n = new Notification({
|
var n = new Notification({
|
||||||
type: type,
|
type: type,
|
||||||
data: data,
|
data: data,
|
||||||
});
|
});
|
||||||
this.storage.storeNotification(this.walletId, n, function () {
|
this.storage.storeNotification(walletId, n, function() {
|
||||||
self.emit(n);
|
self.emit(n);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -202,7 +207,9 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
|
||||||
|
|
||||||
wallet.addCopayer(copayer);
|
wallet.addCopayer(copayer);
|
||||||
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
|
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
|
||||||
self.notify('newCopayer');
|
self._notify('NewCopayer', {
|
||||||
|
walletId: opts.walletId,
|
||||||
|
});
|
||||||
return cb(err, copayer.id);
|
return cb(err, copayer.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -227,6 +234,7 @@ CopayServer.prototype.createAddress = function(opts, cb) {
|
||||||
self.storage.storeAddressAndWallet(wallet, address, function(err) {
|
self.storage.storeAddressAndWallet(wallet, address, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._notify('NewAddress');
|
||||||
return cb(null, address);
|
return cb(null, address);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -411,17 +419,20 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) {
|
||||||
CopayServer.prototype.createTx = function(opts, cb) {
|
CopayServer.prototype.createTx = function(opts, cb) {
|
||||||
var self = this;
|
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) {
|
Utils.runLocked(self.walletId, cb, function(cb) {
|
||||||
self.getWallet({}, function(err, wallet) {
|
self.getWallet({}, function(err, wallet) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
if (!wallet.isComplete()) return cb(new ClientError('Wallet is not complete'));
|
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 copayer = wallet.getCopayer(self.copayerId);
|
||||||
var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message;
|
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;
|
var toAddress;
|
||||||
try {
|
try {
|
||||||
|
@ -429,7 +440,8 @@ CopayServer.prototype.createTx = function(opts, cb) {
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return cb(new ClientError('INVALIDADDRESS', 'Invalid address'));
|
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) {
|
self._getUtxos(function(err, utxos) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
@ -463,6 +475,9 @@ CopayServer.prototype.createTx = function(opts, cb) {
|
||||||
self.storage.storeTx(wallet.id, txp, function(err) {
|
self.storage.storeTx(wallet.id, txp, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._notify('NewTxProposal', {
|
||||||
|
amount: opts.amount
|
||||||
|
});
|
||||||
return cb(null, txp);
|
return cb(null, txp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -538,6 +553,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) {
|
||||||
if (actors.length == 1 && actors[0] !== self.copayerId)
|
if (actors.length == 1 && actors[0] !== self.copayerId)
|
||||||
return cb(new ClientError('Not allowed to erase this TX'));
|
return cb(new ClientError('Not allowed to erase this TX'));
|
||||||
|
|
||||||
|
self._notify('transactionProposalRemoved');
|
||||||
self.storage.removeTx(self.walletId, opts.id, cb);
|
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) {
|
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._notify('TxProposalAcceptedBy', {
|
||||||
|
txProposalId: opts.txProposalId,
|
||||||
|
copayerId: self.copayerId,
|
||||||
|
});
|
||||||
|
|
||||||
if (txp.status == 'accepted') {
|
if (txp.status == 'accepted') {
|
||||||
|
|
||||||
|
self._notify('TxProposalFinallyAccepted', {
|
||||||
|
txProposalId: opts.txProposalId,
|
||||||
|
});
|
||||||
|
|
||||||
self._broadcastTx(txp, function(err, txid) {
|
self._broadcastTx(txp, function(err, txid) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
@ -598,6 +624,11 @@ CopayServer.prototype.signTx = function(opts, cb) {
|
||||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._notify('newOutgoingTx', {
|
||||||
|
txProposalId: opts.txProposalId,
|
||||||
|
txid: txid
|
||||||
|
});
|
||||||
|
|
||||||
return cb(null, txp);
|
return cb(null, txp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -628,14 +659,29 @@ CopayServer.prototype.rejectTx = function(opts, cb) {
|
||||||
var action = _.find(txp.actions, {
|
var action = _.find(txp.actions, {
|
||||||
copayerId: self.copayerId
|
copayerId: self.copayerId
|
||||||
});
|
});
|
||||||
if (action) return cb(new ClientError('CVOTED', 'Copayer already voted on this transaction proposal'));
|
if (action)
|
||||||
if (txp.status != 'pending') return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending'));
|
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);
|
txp.reject(self.copayerId);
|
||||||
|
|
||||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||||
if (err) return cb(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();
|
return cb();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
Storage.prototype._removeAllAddresses = function(walletId, cb) {
|
||||||
|
@ -323,23 +326,11 @@ Storage.prototype.removeWallet = function(walletId, cb) {
|
||||||
async.series([
|
async.series([
|
||||||
|
|
||||||
function(next) {
|
function(next) {
|
||||||
|
// This should be the first step. Will check the wallet exists
|
||||||
self._removeCopayers(walletId, next);
|
self._removeCopayers(walletId, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
self._removeAllAddresses(walletId, next);
|
self._delByKey(walletPrefix(walletId), cb);
|
||||||
},
|
|
||||||
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);
|
|
||||||
},
|
},
|
||||||
], cb);
|
], cb);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1202,6 +1202,7 @@ describe('Copay server', function() {
|
||||||
server.removeWallet({}, function(err) {
|
server.removeWallet({}, function(err) {
|
||||||
i = 0;
|
i = 0;
|
||||||
server.storage._dump(function() {
|
server.storage._dump(function() {
|
||||||
|
server.storage._dump();
|
||||||
i.should.equal(0);
|
i.should.equal(0);
|
||||||
done();
|
done();
|
||||||
}, count);
|
}, count);
|
||||||
|
|
Loading…
Reference in New Issue