refactor server & tests
This commit is contained in:
parent
df84843a96
commit
ddbfcbe7f5
149
lib/server.js
149
lib/server.js
|
@ -25,25 +25,56 @@ var Address = require('./model/address');
|
|||
var TxProposal = require('./model/txproposal');
|
||||
|
||||
|
||||
var initialized = false;
|
||||
var storage;
|
||||
|
||||
/**
|
||||
* Creates an instance of the Copay server.
|
||||
* @constructor
|
||||
*/
|
||||
function CopayServer() {
|
||||
if (!initialized) throw new Error('Server not initialized');
|
||||
this.storage = storage;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes global settings for all instances.
|
||||
* @param {Object} opts
|
||||
* @param {Storage} [opts.storage] - The storage provider.
|
||||
*/
|
||||
function CopayServer(opts) {
|
||||
CopayServer.initialize = function (opts) {
|
||||
opts = opts || {};
|
||||
this.storage = opts.storage || new Storage();
|
||||
storage = opts.storage || new Storage();
|
||||
initialized = true;
|
||||
};
|
||||
|
||||
inherits(CopayServer, events.EventEmitter);
|
||||
/**
|
||||
* Gets an instance of the server after authenticating the copayer.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.copayerId - The copayer id making the request.
|
||||
* @param {string} opts.message - The contents of the request to be signed.
|
||||
* @param {string} opts.signature - Signature of message to be verified using the copayer's signingPubKey.
|
||||
*/
|
||||
CopayServer.getInstanceWithAuth = function (opts, cb) {
|
||||
|
||||
CopayServer._emit = function(event) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
log.debug('Emitting: ', args);
|
||||
this.emit.apply(this, arguments);
|
||||
Utils.checkRequired(opts, ['copayerId', 'message', 'signature']);
|
||||
|
||||
var server = new CopayServer();
|
||||
server.storage.fetchCopayerLookup(opts.copayerId, function (err, copayer) {
|
||||
if (err) return cb(err);
|
||||
if (!copayer) return cb('Copayer not found');
|
||||
|
||||
var isValid = server._verifySignature(opts.message, opts.signature, copayer.signingPubKey);
|
||||
if (!isValid) return cb('Invalid signature');
|
||||
|
||||
server.copayerId = opts.copayerId;
|
||||
server.walletId = copayer.walletId;
|
||||
return cb(null, server);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new wallet.
|
||||
* @param {Object} opts
|
||||
|
@ -89,13 +120,12 @@ CopayServer.prototype.createWallet = function(opts, cb) {
|
|||
/**
|
||||
* Retrieves a wallet from storage.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.id - The wallet id.
|
||||
* @returns {Object} wallet
|
||||
*/
|
||||
CopayServer.prototype.getWallet = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.fetchWallet(opts.id, function(err, wallet) {
|
||||
self.storage.fetchWallet(self.walletId, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
if (!wallet) return cb(new ClientError('Wallet not found'));
|
||||
return cb(null, wallet);
|
||||
|
@ -128,10 +158,9 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
|
|||
Utils.checkRequired(opts, ['walletId', 'id', 'name', 'xPubKey', 'xPubKeySignature']);
|
||||
|
||||
Utils.runLocked(opts.walletId, cb, function(cb) {
|
||||
self.getWallet({
|
||||
id: opts.walletId
|
||||
}, function(err, wallet) {
|
||||
self.storage.fetchWallet(opts.walletId, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
if (!wallet) return cb(new ClientError('Wallet not found'));
|
||||
|
||||
if (!self._verifySignature(opts.xPubKey, opts.xPubKeySignature, wallet.pubKey)) {
|
||||
return cb(new ClientError());
|
||||
|
@ -160,12 +189,8 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
|
|||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* TODO: How this is going to be authenticated?
|
||||
*
|
||||
* Creates a new address.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {truthy} opts.isChange - Indicates whether this is a regular address or a change address.
|
||||
* @returns {Address} address
|
||||
*/
|
||||
|
@ -173,12 +198,10 @@ CopayServer.prototype.createAddress = function(opts, cb) {
|
|||
var self = this;
|
||||
var isChange = opts.isChange || false;
|
||||
|
||||
Utils.checkRequired(opts, ['walletId', 'isChange']);
|
||||
Utils.checkRequired(opts, ['isChange']);
|
||||
|
||||
Utils.runLocked(opts.walletId, cb, function(cb) {
|
||||
self.getWallet({
|
||||
id: opts.walletId
|
||||
}, function(err, wallet) {
|
||||
Utils.runLocked(self.walletId, cb, function(cb) {
|
||||
self.getWallet({}, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var address = wallet.createAddress(opts.isChange);
|
||||
|
@ -203,13 +226,12 @@ CopayServer.prototype.createAddress = function(opts, cb) {
|
|||
/**
|
||||
* Get all addresses.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @returns {Address[]}
|
||||
*/
|
||||
CopayServer.prototype.getAddresses = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.fetchAddresses(opts.walletId, function(err, addresses) {
|
||||
self.storage.fetchAddresses(self.walletId, function(err, addresses) {
|
||||
if (err) return cb(err);
|
||||
|
||||
return cb(null, addresses);
|
||||
|
@ -219,8 +241,6 @@ CopayServer.prototype.getAddresses = function(opts, cb) {
|
|||
/**
|
||||
* Verifies that a given message was actually sent by an authorized copayer.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {string} opts.copayerId - The wallet id.
|
||||
* @param {string} opts.message - The message to verify.
|
||||
* @param {string} opts.signature - The signature of message to verify.
|
||||
* @returns {truthy} The result of the verification.
|
||||
|
@ -228,15 +248,12 @@ CopayServer.prototype.getAddresses = function(opts, cb) {
|
|||
CopayServer.prototype.verifyMessageSignature = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, ['walletId', 'copayerId', 'message', 'signature']);
|
||||
Utils.checkRequired(opts, ['message', 'signature']);
|
||||
|
||||
self.getWallet({
|
||||
id: opts.walletId
|
||||
}, function(err, wallet) {
|
||||
self.getWallet({}, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var copayer = wallet.getCopayer(opts.copayerId);
|
||||
if (!copayer) return cb(new ClientError('Copayer not found'));
|
||||
var copayer = wallet.getCopayer(self.copayerId);
|
||||
|
||||
var isValid = self._verifySignature(opts.message, opts.signature, copayer.signingPubKey);
|
||||
return cb(null, isValid);
|
||||
|
@ -267,14 +284,13 @@ CopayServer.prototype._getBlockExplorer = function(provider, network) {
|
|||
/**
|
||||
* _getUtxos
|
||||
*
|
||||
* @param opts.walletId
|
||||
*/
|
||||
CopayServer.prototype._getUtxos = function(opts, cb) {
|
||||
CopayServer.prototype._getUtxos = function(cb) {
|
||||
var self = this;
|
||||
|
||||
|
||||
// Get addresses for this wallet
|
||||
self.storage.fetchAddresses(opts.walletId, function(err, addresses) {
|
||||
self.storage.fetchAddresses(self.walletId, function(err, addresses) {
|
||||
if (err) return cb(err);
|
||||
if (addresses.length == 0) return cb(new ClientError('The wallet has no addresses'));
|
||||
|
||||
|
@ -286,9 +302,7 @@ CopayServer.prototype._getUtxos = function(opts, cb) {
|
|||
bc.getUnspentUtxos(addressStrs, function(err, utxos) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.getPendingTxs({
|
||||
walletId: opts.walletId
|
||||
}, function(err, txps) {
|
||||
self.getPendingTxs({}, function(err, txps) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var inputs = _.chain(txps)
|
||||
|
@ -326,17 +340,12 @@ CopayServer.prototype._getUtxos = function(opts, cb) {
|
|||
/**
|
||||
* Creates a new transaction proposal.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @returns {Object} balance - Total amount & locked amount.
|
||||
*/
|
||||
CopayServer.prototype.getBalance = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, 'walletId');
|
||||
|
||||
self._getUtxos({
|
||||
walletId: opts.walletId
|
||||
}, function(err, utxos) {
|
||||
self._getUtxos(function(err, utxos) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var balance = {};
|
||||
|
@ -355,7 +364,7 @@ CopayServer.prototype.getBalance = function(opts, cb) {
|
|||
};
|
||||
|
||||
|
||||
|
||||
// TODO: should be in Utils
|
||||
CopayServer.prototype._inputSatoshis = function(i) {
|
||||
return i.amount ? Utils.strip(i.amount * 1e8) : i.satoshis;
|
||||
};
|
||||
|
@ -383,8 +392,6 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) {
|
|||
/**
|
||||
* Creates a new transaction proposal.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {string} opts.copayerId - The wallet id.
|
||||
* @param {string} opts.toAddress - Destination address.
|
||||
* @param {number} opts.amount - Amount to transfer in satoshi.
|
||||
* @param {string} opts.message - A message to attach to this transaction.
|
||||
|
@ -393,21 +400,17 @@ CopayServer.prototype._selectUtxos = function(txp, utxos) {
|
|||
CopayServer.prototype.createTx = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, ['walletId', 'copayerId', 'toAddress', 'amount', 'message']);
|
||||
Utils.checkRequired(opts, ['toAddress', 'amount', 'message']);
|
||||
|
||||
|
||||
// TODO?
|
||||
// Check some parameters like:
|
||||
// amount > dust
|
||||
|
||||
self.getWallet({
|
||||
id: opts.walletId
|
||||
}, function(err, wallet) {
|
||||
self.getWallet({}, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._getUtxos({
|
||||
walletId: wallet.id
|
||||
}, function(err, utxos) {
|
||||
self._getUtxos(function(err, utxos) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var changeAddress = wallet.createAddress(true).address;
|
||||
|
@ -417,7 +420,7 @@ CopayServer.prototype.createTx = function(opts, cb) {
|
|||
});
|
||||
|
||||
var txp = new TxProposal({
|
||||
creatorId: opts.copayerId,
|
||||
creatorId: self.copayerId,
|
||||
toAddress: opts.toAddress,
|
||||
amount: opts.amount,
|
||||
changeAddress: changeAddress,
|
||||
|
@ -446,14 +449,13 @@ CopayServer.prototype.createTx = function(opts, cb) {
|
|||
/**
|
||||
* Retrieves a tx from storage.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {string} opts.id - The tx id.
|
||||
* @returns {Object} txProposal
|
||||
*/
|
||||
CopayServer.prototype.getTx = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
self.storage.fetchTx(opts.walletId, opts.id, function(err, txp) {
|
||||
self.storage.fetchTx(self.walletId, opts.id, function(err, txp) {
|
||||
if (err) return cb(err);
|
||||
if (!txp) return cb(new ClientError('Transaction proposal not found'));
|
||||
return cb(null, txp);
|
||||
|
@ -471,23 +473,18 @@ CopayServer.prototype._broadcastTx = function(txp, cb) {
|
|||
/**
|
||||
* Sign a transaction proposal.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {string} opts.copayerId - The wallet id.
|
||||
* @param {string} opts.txProposalId - The identifier of the transaction.
|
||||
* @param {string} opts.signatures - The signatures of the inputs of this tx for this copayer (in apperance order)
|
||||
*/
|
||||
CopayServer.prototype.signTx = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, ['walletId', 'copayerId', 'txProposalId', 'signatures']);
|
||||
Utils.checkRequired(opts, ['txProposalId', 'signatures']);
|
||||
|
||||
self.getWallet({
|
||||
id: opts.walletId
|
||||
}, function(err, wallet) {
|
||||
self.getWallet({}, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.getTx({
|
||||
walletId: opts.walletId,
|
||||
id: opts.txProposalId
|
||||
}, function(err, txp) {
|
||||
if (err) return cb(err);
|
||||
|
@ -500,22 +497,22 @@ CopayServer.prototype.signTx = function(opts, cb) {
|
|||
if (txp.status != 'pending')
|
||||
return cb(new ClientError('TXNOTPENDING', 'The transaction proposal is not pending'));
|
||||
|
||||
var copayer = wallet.getCopayer(opts.copayerId);
|
||||
var copayer = wallet.getCopayer(self.copayerId);
|
||||
|
||||
if (!txp.checkSignatures(opts.signatures, copayer.xPubKey))
|
||||
return cb(new ClientError('BADSIGNATURES', 'Bad signatures'));
|
||||
|
||||
txp.sign(opts.copayerId, opts.signatures);
|
||||
txp.sign(self.copayerId, opts.signatures);
|
||||
|
||||
self.storage.storeTx(opts.walletId, txp, function(err) {
|
||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (txp.status == 'accepted') {
|
||||
self._broadcastTx(txp, function(err, txid) {
|
||||
if (err) return cb(err, txp);
|
||||
if (err) return cb(err);
|
||||
|
||||
txp.setBroadcasted(txid);
|
||||
self.storage.storeTx(opts.walletId, txp, function(err) {
|
||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
return cb(null, txp);
|
||||
|
@ -532,31 +529,28 @@ CopayServer.prototype.signTx = function(opts, cb) {
|
|||
/**
|
||||
* Reject a transaction proposal.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @param {string} opts.copayerId - The wallet id.
|
||||
* @param {string} opts.txProposalId - The identifier of the transaction.
|
||||
* @param {string} [opts.reason] - A message to other copayers explaining the rejection.
|
||||
*/
|
||||
CopayServer.prototype.rejectTx = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, ['walletId', 'copayerId', 'txProposalId']);
|
||||
Utils.checkRequired(opts, ['txProposalId']);
|
||||
|
||||
self.getTx({
|
||||
walletId: opts.walletId,
|
||||
id: opts.txProposalId
|
||||
}, function(err, txp) {
|
||||
if (err) return cb(err);
|
||||
if (!txp) return cb(new ClientError('Transaction proposal not found'));
|
||||
var action = _.find(txp.actions, {
|
||||
copayerId: opts.copayerId
|
||||
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'));
|
||||
|
||||
txp.reject(opts.copayerId);
|
||||
txp.reject(self.copayerId);
|
||||
|
||||
self.storage.storeTx(opts.walletId, txp, function(err) {
|
||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
return cb();
|
||||
|
@ -567,15 +561,12 @@ CopayServer.prototype.rejectTx = function(opts, cb) {
|
|||
/**
|
||||
* Retrieves all pending transaction proposals.
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.walletId - The wallet id.
|
||||
* @returns {TxProposal[]} Transaction proposal.
|
||||
*/
|
||||
CopayServer.prototype.getPendingTxs = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
Utils.checkRequired(opts, 'walletId');
|
||||
|
||||
self.storage.fetchTxs(opts.walletId, function(err, txps) {
|
||||
self.storage.fetchTxs(self.walletId, function(err, txps) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var pending = _.filter(txps, {
|
||||
|
|
|
@ -60,7 +60,22 @@ var aTextSignature = '3045022100addd20e5413865d65d561ad2979f2289a40d52594b1f8048
|
|||
|
||||
|
||||
var helpers = {};
|
||||
helpers.getAuthServer = function (copayerId, cb) {
|
||||
var signatureStub = sinon.stub(CopayServer.prototype, '_verifySignature');
|
||||
signatureStub.returns(true);
|
||||
CopayServer.getInstanceWithAuth({
|
||||
copayerId: copayerId,
|
||||
message: 'dummy',
|
||||
signature: 'dummy',
|
||||
}, function (err, server) {
|
||||
signatureStub.restore();
|
||||
return cb(server);
|
||||
});
|
||||
};
|
||||
|
||||
helpers.createAndJoinWallet = function(id, m, n, cb) {
|
||||
var server = new CopayServer();
|
||||
|
||||
var walletOpts = {
|
||||
id: id,
|
||||
name: id + ' wallet',
|
||||
|
@ -84,14 +99,13 @@ helpers.createAndJoinWallet = function(id, m, n, cb) {
|
|||
server.joinWallet(copayerOpts, function(err) {
|
||||
return cb(err);
|
||||
});
|
||||
}, function(err) {
|
||||
if (err) return cb(err);
|
||||
}, function (err) {
|
||||
if (err) return new Error('Could not generate wallet');
|
||||
|
||||
server.getWallet({
|
||||
id: id,
|
||||
includeCopayers: true
|
||||
}, function(err, wallet) {
|
||||
return cb(err, wallet);
|
||||
helpers.getAuthServer('1', function (s) {
|
||||
s.getWallet({}, function (err, w) {
|
||||
cb(s, w);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -114,10 +128,8 @@ helpers.toSatoshi = function(btc) {
|
|||
helpers.createUtxos = function(server, wallet, amounts, cb) {
|
||||
var addresses = [];
|
||||
|
||||
|
||||
async.each(amounts, function(a, next) {
|
||||
server.createAddress({
|
||||
walletId: wallet.id,
|
||||
isChange: false,
|
||||
}, function(err, address) {
|
||||
addresses.push(address);
|
||||
|
@ -191,7 +203,6 @@ helpers.clientSign = function(tx, xpriv, n) {
|
|||
};
|
||||
|
||||
var db, storage;
|
||||
var server;
|
||||
|
||||
|
||||
describe('Copay server', function() {
|
||||
|
@ -202,96 +213,27 @@ describe('Copay server', function() {
|
|||
storage = new Storage({
|
||||
db: db
|
||||
});
|
||||
CopayServer.initialize({ storage: storage });
|
||||
});
|
||||
|
||||
describe('#getWallet', function() {
|
||||
describe.skip('#getInstanceWithAuth', function() {
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
});
|
||||
|
||||
it('should get existing wallet', function(done) {
|
||||
|
||||
var w1 = new Wallet({
|
||||
id: '123',
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: aPubKey,
|
||||
it('should get server instance for existing copayer', function(done) {
|
||||
});
|
||||
|
||||
var w2 = new Wallet({
|
||||
id: '234',
|
||||
name: 'my wallet 2',
|
||||
m: 3,
|
||||
n: 4,
|
||||
pubKey: aPubKey,
|
||||
it('should fail when requesting for non-existent copayer', function(done) {
|
||||
});
|
||||
|
||||
db.batch([{
|
||||
type: 'put',
|
||||
key: 'wallet-123',
|
||||
value: w1,
|
||||
}, {
|
||||
type: 'put',
|
||||
key: 'wallet-234',
|
||||
value: w2,
|
||||
}]);
|
||||
|
||||
server.getWallet({
|
||||
id: '123',
|
||||
includeCopayers: true
|
||||
}, function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.id.should.equal('123');
|
||||
wallet.name.should.equal('my wallet');
|
||||
wallet.status.should.equal('pending');
|
||||
wallet.copayers.length.should.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when requesting non-existent wallet', function(done) {
|
||||
var w1 = new Wallet({
|
||||
id: '123',
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: aPubKey,
|
||||
});
|
||||
var w2 = new Wallet({
|
||||
id: '234',
|
||||
name: 'my wallet 2',
|
||||
m: 3,
|
||||
n: 4,
|
||||
pubKey: aPubKey,
|
||||
});
|
||||
db.batch([{
|
||||
type: 'put',
|
||||
key: 'wallet-123',
|
||||
value: w1,
|
||||
}, {
|
||||
type: 'put',
|
||||
key: 'wallet-234',
|
||||
value: w2,
|
||||
}]);
|
||||
|
||||
server.getWallet({
|
||||
id: '345'
|
||||
}, function(err, wallet) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('Wallet not found');
|
||||
done();
|
||||
});
|
||||
it('should fail when message signature cannot be verified', function(done) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createWallet', function() {
|
||||
var server;
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
server = new CopayServer();
|
||||
});
|
||||
|
||||
it('should create and store wallet', function(done) {
|
||||
|
@ -304,9 +246,7 @@ describe('Copay server', function() {
|
|||
};
|
||||
server.createWallet(opts, function(err) {
|
||||
should.not.exist(err);
|
||||
server.getWallet({
|
||||
id: '123'
|
||||
}, function(err, wallet) {
|
||||
server.storage.fetchWallet('123', function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.id.should.equal('123');
|
||||
wallet.name.should.equal('my wallet');
|
||||
|
@ -325,9 +265,7 @@ describe('Copay server', function() {
|
|||
};
|
||||
server.createWallet(opts, function(err) {
|
||||
should.not.exist(err);
|
||||
server.getWallet({
|
||||
id: '123'
|
||||
}, function(err, wallet) {
|
||||
server.storage.fetchWallet('123', function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.id.should.equal('123');
|
||||
wallet.name.should.equal('my wallet');
|
||||
|
@ -379,10 +317,9 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
describe('#joinWallet', function() {
|
||||
var server;
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
server = new CopayServer();
|
||||
});
|
||||
|
||||
it('should join existing wallet', function(done) {
|
||||
|
@ -405,10 +342,8 @@ describe('Copay server', function() {
|
|||
};
|
||||
server.joinWallet(copayerOpts, function(err) {
|
||||
should.not.exist(err);
|
||||
server.getWallet({
|
||||
id: '123',
|
||||
includeCopayers: true
|
||||
}, function(err, wallet) {
|
||||
helpers.getAuthServer('999', function (server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
wallet.id.should.equal('123');
|
||||
wallet.copayers.length.should.equal(1);
|
||||
var copayer = wallet.copayers[0];
|
||||
|
@ -419,6 +354,7 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to join non-existent wallet', function(done) {
|
||||
var walletOpts = {
|
||||
|
@ -470,9 +406,8 @@ describe('Copay server', function() {
|
|||
};
|
||||
server.joinWallet(copayer1Opts, function(err) {
|
||||
should.not.exist(err);
|
||||
server.getWallet({
|
||||
id: '123'
|
||||
}, function(err, wallet) {
|
||||
helpers.getAuthServer('111', function (server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
wallet.status.should.equal('complete');
|
||||
server.joinWallet(copayer2Opts, function(err) {
|
||||
should.exist(err);
|
||||
|
@ -484,6 +419,7 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to re-join wallet', function(done) {
|
||||
var walletOpts = {
|
||||
|
@ -590,10 +526,8 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should set pkr and status = complete on last copayer joining (2-3)', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 3, function(err, wallet) {
|
||||
server.getWallet({
|
||||
id: '123'
|
||||
}, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 3, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.status.should.equal('complete');
|
||||
wallet.publicKeyRing.length.should.equal(3);
|
||||
|
@ -606,16 +540,11 @@ describe('Copay server', function() {
|
|||
|
||||
describe('#verifyMessageSignature', function() {
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully verify message signature', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
var opts = {
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
message: aText,
|
||||
signature: aTextSignature,
|
||||
};
|
||||
|
@ -627,33 +556,30 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should fail to verify message signature when copayer does not exist', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
it('should fail to verify message signature for different copayer', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function() {
|
||||
var opts = {
|
||||
walletId: '123',
|
||||
copayerId: '999',
|
||||
message: 'hello world',
|
||||
signature: 'dummy',
|
||||
message: aText,
|
||||
signature: aTextSignature,
|
||||
};
|
||||
helpers.getAuthServer('2', function (server) {
|
||||
server.verifyMessageSignature(opts, function(err, isValid) {
|
||||
err.message.should.equal('Copayer not found');
|
||||
should.not.exist(err);
|
||||
isValid.should.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createAddress', function() {
|
||||
beforeEach(function() {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
});
|
||||
|
||||
it('should create main address', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: false,
|
||||
}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
|
@ -667,9 +593,8 @@ describe('Copay server', function() {
|
|||
|
||||
|
||||
it('should create change address', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: true,
|
||||
}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
|
@ -682,10 +607,9 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should create many addresses on simultaneous requests', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
async.map(_.range(10), function(i, cb) {
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: false,
|
||||
}, cb);
|
||||
}, function(err, addresses) {
|
||||
|
@ -700,26 +624,22 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should not create address if unable to store wallet', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
|
||||
var storeWalletStub = sinon.stub(server.storage, 'storeWallet');
|
||||
storeWalletStub.yields('dummy error');
|
||||
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: true,
|
||||
}, function(err, address) {
|
||||
err.should.exist;
|
||||
should.not.exist(address);
|
||||
|
||||
server.getAddresses({
|
||||
walletId: '123'
|
||||
}, function(err, addresses) {
|
||||
server.getAddresses({}, function(err, addresses) {
|
||||
addresses.length.should.equal(0);
|
||||
|
||||
server.storage.storeWallet.restore();
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: true,
|
||||
}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
|
@ -734,26 +654,22 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should not create address if unable to store addresses', function(done) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(server) {
|
||||
|
||||
var storeAddressStub = sinon.stub(server.storage, 'storeAddress');
|
||||
storeAddressStub.yields('dummy error');
|
||||
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: true,
|
||||
}, function(err, address) {
|
||||
err.should.exist;
|
||||
should.not.exist(address);
|
||||
|
||||
server.getAddresses({
|
||||
walletId: '123'
|
||||
}, function(err, addresses) {
|
||||
server.getAddresses({}, function(err, addresses) {
|
||||
addresses.length.should.equal(0);
|
||||
|
||||
server.storage.storeAddress.restore();
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: true,
|
||||
}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
|
@ -769,15 +685,12 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
describe('#createTx', function() {
|
||||
var wallet;
|
||||
var server, wallet;
|
||||
beforeEach(function(done) {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, w) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: false,
|
||||
}, function(err, address) {
|
||||
done();
|
||||
|
@ -786,14 +699,9 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should create tx', function(done) {
|
||||
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([100, 200]), function(utxos) {
|
||||
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
|
||||
var txOpts = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(80),
|
||||
message: 'some message',
|
||||
|
@ -806,14 +714,10 @@ describe('Copay server', function() {
|
|||
tx.should.exist;
|
||||
tx.isAccepted().should.equal.false;
|
||||
tx.isRejected().should.equal.false;
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(1);
|
||||
server.getBalance({
|
||||
walletId: '123'
|
||||
}, function(err, balance) {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(300));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(100));
|
||||
|
@ -825,13 +729,9 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should fail to create tx when insufficient funds', function(done) {
|
||||
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([100]), function(utxos) {
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
|
||||
var txOpts = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(120),
|
||||
message: 'some message',
|
||||
|
@ -842,14 +742,10 @@ describe('Copay server', function() {
|
|||
server.createTx(txOpts, function(err, tx) {
|
||||
err.code.should.equal('INSUFFICIENTFUNDS');
|
||||
err.message.should.equal('Insufficient funds');
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(0);
|
||||
server.getBalance({
|
||||
walletId: '123'
|
||||
}, function(err, balance) {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalAmount.should.equal(10000000000);
|
||||
|
@ -861,13 +757,9 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should create tx when there is a pending tx and enough UTXOs', function(done) {
|
||||
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([10.1, 10.2, 10.3]), function(utxos) {
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
|
||||
var txOpts = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(12),
|
||||
message: 'some message',
|
||||
|
@ -879,8 +771,6 @@ describe('Copay server', function() {
|
|||
tx.should.exist;
|
||||
|
||||
var txOpts2 = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 8,
|
||||
message: 'some message 2',
|
||||
|
@ -890,14 +780,10 @@ describe('Copay server', function() {
|
|||
server.createTx(txOpts2, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
tx.should.exist;
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(2);
|
||||
server.getBalance({
|
||||
walletId: '123'
|
||||
}, function(err, balance) {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(3060000000);
|
||||
balance.lockedAmount.should.equal(3060000000);
|
||||
|
@ -911,11 +797,8 @@ describe('Copay server', function() {
|
|||
|
||||
it('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) {
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([10.1, 10.2, 10.3]), function(utxos) {
|
||||
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
var txOpts = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(12),
|
||||
message: 'some message',
|
||||
|
@ -927,8 +810,6 @@ describe('Copay server', function() {
|
|||
tx.should.exist;
|
||||
|
||||
var txOpts2 = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(24),
|
||||
message: 'some message 2',
|
||||
|
@ -939,14 +820,10 @@ describe('Copay server', function() {
|
|||
err.code.should.equal('INSUFFICIENTFUNDS');
|
||||
err.message.should.equal('Insufficient funds');
|
||||
should.not.exist(tx);
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(1);
|
||||
server.getBalance({
|
||||
walletId: '123'
|
||||
}, function(err, balance) {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(30.6));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(20.3));
|
||||
|
@ -961,23 +838,18 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
describe('#signTx', function() {
|
||||
var wallet, txid;
|
||||
var server, wallet, txid;
|
||||
|
||||
beforeEach(function(done) {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(err, w) {
|
||||
helpers.createAndJoinWallet('123', 2, 2, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: false,
|
||||
}, function(err, address) {
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(utxos) {
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
var txOpts = {
|
||||
copayerId: '1',
|
||||
walletId: '123',
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: helpers.toSatoshi(10),
|
||||
message: 'some message',
|
||||
|
@ -996,16 +868,12 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should sign a TX with multiple inputs, different paths', function(done) {
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
var tx = txs[0];
|
||||
tx.id.should.equal(txid);
|
||||
|
||||
var signatures = helpers.clientSign(tx, someXPrivKey[0], wallet.n);
|
||||
server.signTx({
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
txProposalId: txid,
|
||||
signatures: signatures,
|
||||
}, function(err) {
|
||||
|
@ -1016,9 +884,7 @@ describe('Copay server', function() {
|
|||
});
|
||||
|
||||
it('should fail if one signature is broken', function(done) {
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
var tx = txs[0];
|
||||
tx.id.should.equal(txid);
|
||||
|
||||
|
@ -1026,8 +892,6 @@ describe('Copay server', function() {
|
|||
signatures[0] = 1;
|
||||
|
||||
server.signTx({
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
txProposalId: txid,
|
||||
signatures: signatures,
|
||||
}, function(err) {
|
||||
|
@ -1037,16 +901,12 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
it('should fail on invalids signature', function(done) {
|
||||
server.getPendingTxs({
|
||||
walletId: '123'
|
||||
}, function(err, txs) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
var tx = txs[0];
|
||||
tx.id.should.equal(txid);
|
||||
|
||||
var signatures = ['11', '22', '33', '44'];
|
||||
server.signTx({
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
txProposalId: txid,
|
||||
signatures: signatures,
|
||||
}, function(err) {
|
||||
|
@ -1060,16 +920,13 @@ describe('Copay server', function() {
|
|||
|
||||
|
||||
describe('#signTx and broadcast', function() {
|
||||
var wallet, utxos;
|
||||
var server, wallet, utxos;
|
||||
|
||||
beforeEach(function(done) {
|
||||
server = new CopayServer({
|
||||
storage: storage,
|
||||
});
|
||||
helpers.createAndJoinWallet('123', 1, 1, function(err, w) {
|
||||
helpers.createAndJoinWallet('123', 1, 1, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
server.createAddress({
|
||||
walletId: '123',
|
||||
isChange: false,
|
||||
}, function(err, address) {
|
||||
helpers.createUtxos(server, wallet, helpers.toSatoshi([1, 2, 3, 4, 5, 6, 7, 8]), function(inutxos) {
|
||||
|
@ -1142,8 +999,6 @@ describe('Copay server', function() {
|
|||
txp.id.should.equal(txpid);
|
||||
var signatures = helpers.clientSign(txp, someXPrivKey[0], wallet.n);
|
||||
server.signTx({
|
||||
walletId: '123',
|
||||
copayerId: '1',
|
||||
txProposalId: txpid,
|
||||
signatures: signatures,
|
||||
}, function(err, txp) {
|
||||
|
|
Loading…
Reference in New Issue