Merge pull request #260 from isocolsky/ref/proposal_signature
Ref/proposal signature
This commit is contained in:
commit
a9d1a3c06a
|
@ -18,7 +18,7 @@ TxProposal.Types = {
|
|||
};
|
||||
|
||||
TxProposal.isTypeSupported = function(type) {
|
||||
return !type || _.contains(_.values(TxProposal.Types), type);
|
||||
return _.contains(_.values(TxProposal.Types), type);
|
||||
};
|
||||
|
||||
TxProposal._create = {};
|
||||
|
@ -27,15 +27,21 @@ TxProposal._create.simple = function(txp, opts) {
|
|||
txp.toAddress = opts.toAddress;
|
||||
txp.amount = opts.amount;
|
||||
txp.outputOrder = _.shuffle(_.range(2));
|
||||
try {
|
||||
txp.network = Bitcore.Address(txp.toAddress).toObject().network;
|
||||
} catch (ex) {}
|
||||
};
|
||||
|
||||
TxProposal._create.undefined = TxProposal._create.simple;
|
||||
|
||||
TxProposal._create.multiple_outputs = function(txp, opts) {
|
||||
txp.outputs = opts.outputs;
|
||||
txp.outputs = _.map(opts.outputs, function(output) {
|
||||
return _.pick(output, ['amount', 'toAddress', 'message']);
|
||||
});
|
||||
txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1));
|
||||
try {
|
||||
txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network;
|
||||
} catch (ex) {}
|
||||
};
|
||||
|
||||
TxProposal.create = function(opts) {
|
||||
|
@ -43,10 +49,8 @@ TxProposal.create = function(opts) {
|
|||
|
||||
var x = new TxProposal();
|
||||
|
||||
if (opts.version == '1.0.0' && !opts.type) {
|
||||
opts.type = TxProposal.Types.SIMPLE;
|
||||
}
|
||||
x.type = opts.type;
|
||||
x.type = opts.type || TxProposal.Types.SIMPLE;
|
||||
|
||||
var now = Date.now();
|
||||
x.createdOn = Math.floor(now / 1000);
|
||||
x.id = _.padLeft(now, 14, '0') + Uuid.v4();
|
||||
|
@ -65,7 +69,7 @@ TxProposal.create = function(opts) {
|
|||
x.fee = null;
|
||||
x.feePerKb = opts.feePerKb;
|
||||
|
||||
if (typeof TxProposal._create[x.type] == 'function') {
|
||||
if (_.isFunction(TxProposal._create[x.type])) {
|
||||
TxProposal._create[x.type](x, opts);
|
||||
}
|
||||
|
||||
|
@ -75,10 +79,11 @@ TxProposal.create = function(opts) {
|
|||
TxProposal.fromObj = function(obj) {
|
||||
var x = new TxProposal();
|
||||
|
||||
if (obj.version == '1.0.0' && !obj.type) {
|
||||
obj.type = TxProposal.Types.SIMPLE;
|
||||
}
|
||||
if (obj.version == '1.0.0') {
|
||||
x.type = TxProposal.Types.SIMPLE;
|
||||
} else {
|
||||
x.type = obj.type;
|
||||
}
|
||||
x.version = obj.version;
|
||||
x.createdOn = obj.createdOn;
|
||||
x.id = obj.id;
|
||||
|
@ -161,22 +166,6 @@ TxProposal.prototype.getRawTx = function() {
|
|||
return t.uncheckedSerialize();
|
||||
};
|
||||
|
||||
/**
|
||||
* getHeader
|
||||
*
|
||||
* @return {Array} arguments for getProposalHash wallet utility method
|
||||
*/
|
||||
TxProposal.prototype.getHeader = function() {
|
||||
if (this.type == TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
return [ {
|
||||
outputs: this.outputs,
|
||||
message: this.message,
|
||||
payProUrl: this.payProUrl
|
||||
} ];
|
||||
} else {
|
||||
return [ this.toAddress, this.amount, this.message, this.payProUrl ];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* getTotalAmount
|
||||
|
@ -185,8 +174,10 @@ TxProposal.prototype.getHeader = function() {
|
|||
*/
|
||||
TxProposal.prototype.getTotalAmount = function() {
|
||||
if (this.type == TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
return _.map(this.outputs, function(o) { return o.amount })
|
||||
.reduce(function(total, n) { return total + n; });
|
||||
return _.pluck(this.outputs, 'amount')
|
||||
.reduce(function(total, n) {
|
||||
return total + n;
|
||||
});
|
||||
} else {
|
||||
return this.amount;
|
||||
}
|
||||
|
|
|
@ -827,38 +827,28 @@ WalletService.prototype._canCreateTx = function(copayerId, cb) {
|
|||
WalletService.prototype.createTx = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
if (!Utils.checkRequired(opts, ['proposalSignature']))
|
||||
if (!opts.outputs) {
|
||||
opts.outputs = _.pick(opts, ['amount', 'toAddress']);
|
||||
}
|
||||
opts.outputs = [].concat(opts.outputs);
|
||||
|
||||
if (!Utils.checkRequired(opts, ['outputs', 'proposalSignature']))
|
||||
return cb(new ClientError('Required argument missing'));
|
||||
|
||||
if (!Model.TxProposal.isTypeSupported(opts.type))
|
||||
var type = opts.type || Model.TxProposal.Types.SIMPLE;
|
||||
if (!Model.TxProposal.isTypeSupported(type))
|
||||
return cb(new ClientError('Invalid proposal type'));
|
||||
|
||||
if (opts.type == Model.TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
if (!Utils.checkRequired(opts, ['outputs']))
|
||||
return cb(new ClientError('Required argument missing'));
|
||||
_.each(opts.outputs, function(o) {
|
||||
o.valid = true;
|
||||
if (!Utils.checkRequired(o, ['toAddress', 'amount'])) {
|
||||
o.valid = false;
|
||||
_.each(opts.outputs, function(output) {
|
||||
if (!Utils.checkRequired(output, ['toAddress', 'amount'])) {
|
||||
output.valid = false;
|
||||
cb(new ClientError('Required outputs argument missing'));
|
||||
return false;
|
||||
}
|
||||
_.each(_.keys(o), function(key) {
|
||||
if (!_.contains(['toAddress', 'amount', 'message', 'valid'], key)) {
|
||||
o.valid = false;
|
||||
cb(new ClientError('Invalid outputs argument found'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (!o.valid) return false;
|
||||
});
|
||||
if (_.any(opts.outputs, 'valid', false)) return;
|
||||
_.each(opts.outputs, function(o) { delete o.valid; });
|
||||
|
||||
} else {
|
||||
if (!Utils.checkRequired(opts, ['toAddress', 'amount']))
|
||||
return cb(new ClientError('Required argument missing'));
|
||||
}
|
||||
if (_.any(opts.outputs, {
|
||||
valid: false
|
||||
})) return;
|
||||
|
||||
var feePerKb = opts.feePerKb || 10000;
|
||||
if (feePerKb < WalletUtils.MIN_FEE_PER_KB || feePerKb > WalletUtils.MAX_FEE_PER_KB)
|
||||
|
@ -870,21 +860,30 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
if (!wallet.isComplete())
|
||||
return cb(new ClientError('Wallet is not complete'));
|
||||
|
||||
var copayer = wallet.getCopayer(self.copayerId);
|
||||
var hash;
|
||||
if (!opts.type) {
|
||||
hash = WalletUtils.getProposalHash(opts.toAddress, opts.amount, opts.message, opts.payProUrl);
|
||||
} else {
|
||||
var header = {
|
||||
outputs: _.map(opts.outputs, function(output) {
|
||||
return _.pick(output, ['toAddress', 'amount', 'message']);
|
||||
}),
|
||||
message: opts.message,
|
||||
payProUrl: opts.payProUrl
|
||||
};
|
||||
hash = WalletUtils.getProposalHash(header)
|
||||
}
|
||||
|
||||
if (!self._verifySignature(hash, opts.proposalSignature, copayer.requestPubKey))
|
||||
return cb(new ClientError('Invalid proposal signature'));
|
||||
|
||||
self._canCreateTx(self.copayerId, function(err, canCreate) {
|
||||
if (err) return cb(err);
|
||||
if (!canCreate)
|
||||
return cb(new ClientError('NOTALLOWEDTOCREATETX', 'Cannot create TX proposal during backoff time'));
|
||||
|
||||
var copayer = wallet.getCopayer(self.copayerId);
|
||||
var proposalHeader = Model.TxProposal.create(opts).getHeader();
|
||||
var hash = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader);
|
||||
if (!self._verifySignature(hash, opts.proposalSignature, copayer.requestPubKey))
|
||||
return cb(new ClientError('Invalid proposal signature'));
|
||||
|
||||
var outputs = (opts.type == Model.TxProposal.Types.MULTIPLEOUTPUTS)
|
||||
? opts.outputs
|
||||
: [ { toAddress: opts.toAddress, amount: opts.amount } ];
|
||||
_.each(outputs, function(output) {
|
||||
_.each(opts.outputs, function(output) {
|
||||
output.valid = false;
|
||||
var toAddress = {};
|
||||
try {
|
||||
|
@ -897,7 +896,7 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
cb(new ClientError('INVALIDADDRESS', 'Incorrect address network'));
|
||||
return false;
|
||||
}
|
||||
if (output.amount <= 0) {
|
||||
if (!_.isNumber(output.amount) || _.isNaN(output.amount) || output.amount <= 0) {
|
||||
cb(new ClientError('Invalid amount'));
|
||||
return false;
|
||||
}
|
||||
|
@ -907,12 +906,12 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
}
|
||||
output.valid = true;
|
||||
});
|
||||
if (_.any(outputs, 'valid', false)) return;
|
||||
|
||||
var changeAddress = wallet.createAddress(true);
|
||||
if (_.any(opts.outputs, {
|
||||
valid: false
|
||||
})) return;
|
||||
|
||||
var txp = Model.TxProposal.create({
|
||||
type: opts.type,
|
||||
type: type,
|
||||
walletId: self.walletId,
|
||||
creatorId: self.copayerId,
|
||||
outputs: opts.outputs,
|
||||
|
@ -920,20 +919,19 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
amount: opts.amount,
|
||||
message: opts.message,
|
||||
proposalSignature: opts.proposalSignature,
|
||||
changeAddress: wallet.createAddress(true),
|
||||
feePerKb: feePerKb,
|
||||
payProUrl: opts.payProUrl,
|
||||
changeAddress: changeAddress,
|
||||
requiredSignatures: wallet.m,
|
||||
requiredRejections: Math.min(wallet.m, wallet.n - wallet.m + 1),
|
||||
});
|
||||
|
||||
|
||||
self._selectTxInputs(txp, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
$.checkState(txp.inputs);
|
||||
|
||||
self.storage.storeAddressAndWallet(wallet, changeAddress, function(err) {
|
||||
self.storage.storeAddressAndWallet(wallet, txp.changeAddress, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self.storage.storeTx(wallet.id, txp, function(err) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "bitcore-wallet-service",
|
||||
"description": "A service for Mutisig HD Bitcoin Wallets",
|
||||
"author": "BitPay Inc",
|
||||
"version": "0.0.36",
|
||||
"version": "0.0.37",
|
||||
"keywords": [
|
||||
"bitcoin",
|
||||
"copay",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"dependencies": {
|
||||
"async": "^0.9.0",
|
||||
"bitcore": "^0.12.9",
|
||||
"bitcore-wallet-utils": "^0.0.17",
|
||||
"bitcore-wallet-utils": "^0.0.19",
|
||||
"body-parser": "^1.11.0",
|
||||
"coveralls": "^2.11.2",
|
||||
"email-validator": "^1.0.1",
|
||||
|
|
|
@ -137,7 +137,7 @@ helpers.toSatoshi = function(btc) {
|
|||
helpers.stubUtxos = function(server, wallet, amounts, cb) {
|
||||
var amounts = [].concat(amounts);
|
||||
|
||||
async.map(_.range(1, Math.ceil(amounts.length / 2) + 1), function(i, next) {
|
||||
async.mapSeries(_.range(1, Math.ceil(amounts.length / 2) + 1), function(i, next) {
|
||||
server.createAddress({}, function(err, address) {
|
||||
next(err, address);
|
||||
});
|
||||
|
@ -184,7 +184,7 @@ helpers.stubAddressActivity = function(activeAddresses) {
|
|||
|
||||
helpers.clientSign = WalletUtils.signTxp;
|
||||
|
||||
helpers.createProposalOpts = function(toAddress, amount, message, signingKey, feePerKb) {
|
||||
helpers.createProposalOptsLegacy = function(toAddress, amount, message, signingKey, feePerKb) {
|
||||
var opts = {
|
||||
toAddress: toAddress,
|
||||
amount: helpers.toSatoshi(amount),
|
||||
|
@ -193,7 +193,8 @@ helpers.createProposalOpts = function(toAddress, amount, message, signingKey, fe
|
|||
};
|
||||
if (feePerKb) opts.feePerKb = feePerKb;
|
||||
|
||||
var hash = WalletUtils.getProposalHash(opts.toAddress, opts.amount, opts.message);
|
||||
var hash = WalletUtils.getProposalHash(toAddress, opts.amount, message);
|
||||
|
||||
try {
|
||||
opts.proposalSignature = WalletUtils.signMessage(hash, signingKey);
|
||||
} catch (ex) {}
|
||||
|
@ -201,27 +202,41 @@ helpers.createProposalOpts = function(toAddress, amount, message, signingKey, fe
|
|||
return opts;
|
||||
};
|
||||
|
||||
helpers.createProposalOptsByType = function(type, outputs, message, signingKey, feePerKb) {
|
||||
helpers.createSimpleProposalOpts = function(toAddress, amount, message, signingKey, feePerKb) {
|
||||
var outputs = [{
|
||||
toAddress: toAddress,
|
||||
amount: amount,
|
||||
}];
|
||||
return helpers.createProposalOpts(Model.TxProposal.Types.SIMPLE, outputs, message, signingKey, feePerKb);
|
||||
};
|
||||
|
||||
helpers.createProposalOpts = function(type, outputs, message, signingKey, feePerKb) {
|
||||
_.each(outputs, function(output) {
|
||||
output.amount = helpers.toSatoshi(output.amount);
|
||||
});
|
||||
var opts = {
|
||||
type: type,
|
||||
message: message,
|
||||
proposalSignature: null,
|
||||
};
|
||||
if (type == Model.TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
opts.outputs = [];
|
||||
_.each(outputs, function(o) {
|
||||
opts.outputs.push(o);
|
||||
o.amount = helpers.toSatoshi(o.amount);
|
||||
});
|
||||
} else {
|
||||
opts.toAddress = outputs[0].toAddress;
|
||||
opts.amount = helpers.toSatoshi(outputs[0].amount);
|
||||
}
|
||||
if (feePerKb) opts.feePerKb = feePerKb;
|
||||
|
||||
var txp = Model.TxProposal.create(opts);
|
||||
var proposalHeader = txp.getHeader();
|
||||
var hash = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader);
|
||||
switch (type) {
|
||||
case Model.TxProposal.Types.SIMPLE:
|
||||
opts.toAddress = outputs[0].toAddress;
|
||||
opts.amount = outputs[0].amount;
|
||||
break;
|
||||
case Model.TxProposal.Types.MULTIPLEOUTPUTS:
|
||||
opts.outputs = outputs;
|
||||
break;
|
||||
}
|
||||
|
||||
var header = {
|
||||
outputs: outputs,
|
||||
message: opts.message,
|
||||
payProUrl: opts.payProUrl
|
||||
};
|
||||
var hash = WalletUtils.getProposalHash(header);
|
||||
|
||||
try {
|
||||
opts.proposalSignature = WalletUtils.signMessage(hash, signingKey);
|
||||
|
@ -335,7 +350,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should notify copayers a new tx proposal has been created', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [1, 1], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
setTimeout(function() {
|
||||
|
@ -362,7 +377,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should notify copayers a new outgoing tx has been created', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [1, 1], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
|
||||
var txpId;
|
||||
async.waterfall([
|
||||
|
@ -415,7 +430,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should notify copayers a tx has been finally rejected', function(done) {
|
||||
helpers.stubUtxos(server, wallet, 1, function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
|
||||
var txpId;
|
||||
async.waterfall([
|
||||
|
@ -541,7 +556,7 @@ describe('Wallet service', function() {
|
|||
},
|
||||
}, function(err) {
|
||||
helpers.stubUtxos(server, wallet, 1, function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.8, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
setTimeout(function() {
|
||||
|
@ -1275,7 +1290,7 @@ describe('Wallet service', function() {
|
|||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
helpers.getAuthServer(result.copayerId, function(server, wallet) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
should.exist(err);
|
||||
|
@ -1300,7 +1315,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should create a tx', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1335,9 +1350,20 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should create a tx with legacy signature', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOptsLegacy('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a tx using the uxtos with minimum amount first', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200, 300], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 150, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 150, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1359,7 +1385,7 @@ describe('Wallet service', function() {
|
|||
blockchainExplorer.getUnspentUtxos = sinon.stub().callsArgWith(1, 'dummy error');
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('BLOCKCHAINERROR');
|
||||
|
@ -1370,7 +1396,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx with invalid proposal signature', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, 'dummy');
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, 'dummy');
|
||||
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
|
@ -1383,7 +1409,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx with proposal signed by another copayer', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[1].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[1].privKey_1H_0);
|
||||
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
|
@ -1396,11 +1422,11 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx for invalid address', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('invalid address', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('invalid address', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
should.exist(err);
|
||||
should.not.exist(tx);
|
||||
// may fail due to Non-base58 character, or Checksum mismatch, or other
|
||||
done();
|
||||
});
|
||||
|
@ -1409,7 +1435,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx for address of different network', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('myE38JHdxmQcTJGP1ZiX4BiGhDxMJDvLJD', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('myE38JHdxmQcTJGP1ZiX4BiGhDxMJDvLJD', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
|
@ -1422,7 +1448,7 @@ describe('Wallet service', function() {
|
|||
});
|
||||
|
||||
it('should fail to create tx for invalid amount', function(done) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(tx);
|
||||
should.exist(err);
|
||||
|
@ -1433,7 +1459,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx when insufficient funds', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('INSUFFICIENTFUNDS');
|
||||
|
@ -1454,7 +1480,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx when insufficient funds for fee', function(done) {
|
||||
helpers.stubUtxos(server, wallet, 0.048222, function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.048200, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.048200, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('INSUFFICIENTFUNDS');
|
||||
|
@ -1466,7 +1492,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should scale fees according to tx size', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [1, 1, 1, 1], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 3.5, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 3.5, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
tx.getBitcoreTx()._estimateSize().should.be.within(1001, 1999);
|
||||
|
@ -1478,11 +1504,11 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should be possible to use a smaller fee', function(done) {
|
||||
helpers.stubUtxos(server, wallet, 1, function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('INSUFFICIENTFUNDS');
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, null, TestData.copayers[0].privKey_1H_0, 5000);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
tx.fee.should.equal(5000);
|
||||
|
@ -1502,7 +1528,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx for dust amount', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [1], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.00000001, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.00000001, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('DUSTAMOUNT');
|
||||
|
@ -1518,7 +1544,7 @@ describe('Wallet service', function() {
|
|||
var change = 0.00000001;
|
||||
var amount = 1 - fee - change;
|
||||
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('DUSTAMOUNT');
|
||||
|
@ -1530,14 +1556,14 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail with different error for insufficient funds and locked funds', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [10, 10], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 11, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 11, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(20));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(20));
|
||||
txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey_1H_0);
|
||||
txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('LOCKEDFUNDS');
|
||||
|
@ -1554,7 +1580,7 @@ describe('Wallet service', function() {
|
|||
var fee = Bitcore.Transaction.FEE_PER_KB / 1e8;
|
||||
var amount = 1 - fee;
|
||||
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1573,7 +1599,7 @@ describe('Wallet service', function() {
|
|||
name: 'dummy',
|
||||
message: 'dummy exception'
|
||||
});
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.message.should.equal('dummy exception');
|
||||
|
@ -1585,11 +1611,11 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should create tx when there is a pending tx and enough UTXOs', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [10.1, 10.2, 10.3], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts2 = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts2, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1610,11 +1636,11 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [10.1, 10.2, 10.3], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 24, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts2 = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 24, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts2, function(err, tx) {
|
||||
err.code.should.equal('LOCKEDFUNDS');
|
||||
should.not.exist(tx);
|
||||
|
@ -1640,7 +1666,7 @@ describe('Wallet service', function() {
|
|||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(N * 100));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(0));
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey_1H_0);
|
||||
async.map(_.range(N), function(i, cb) {
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
cb(err, tx);
|
||||
|
@ -1664,11 +1690,16 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should create tx for type multiple_outputs', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var outputs = [
|
||||
{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount: 75, message: 'message #1' },
|
||||
{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount: 75, message: 'message #2' }
|
||||
];
|
||||
var txOpts = helpers.createProposalOptsByType(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var outputs = [{
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 75,
|
||||
message: 'message #1'
|
||||
}, {
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 75,
|
||||
message: 'message #2'
|
||||
}];
|
||||
var txOpts = helpers.createProposalOpts(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1677,16 +1708,20 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should fail to create tx for type multiple_outputs with invalid output argument', function(done) {
|
||||
it('should fail to create tx for type multiple_outputs with missing output argument', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var outputs = [
|
||||
{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount: 80, message: 'message #1', foo: 'bar' },
|
||||
{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', amount: 90, message: 'message #2' }
|
||||
];
|
||||
var txOpts = helpers.createProposalOptsByType(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var outputs = [{
|
||||
amount: 80,
|
||||
message: 'message #1',
|
||||
}, {
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 90,
|
||||
message: 'message #2'
|
||||
}];
|
||||
var txOpts = helpers.createProposalOpts(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.message.should.contain('Invalid outputs argument');
|
||||
err.message.should.contain('outputs argument missing');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -1694,7 +1729,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to create tx for unsupported proposal type', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
txOpts.type = 'bogus';
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
|
@ -1712,7 +1747,7 @@ describe('Wallet service', function() {
|
|||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalKbToSendMax.should.equal(3);
|
||||
var max = (balance.totalAmount - balance.lockedAmount) - (balance.totalKbToSendMax * 10000);
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1729,7 +1764,7 @@ describe('Wallet service', function() {
|
|||
});
|
||||
it('should be able to send max non-locked amount', function(done) {
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 10, 0), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 3.5, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 3.5, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
|
@ -1738,7 +1773,7 @@ describe('Wallet service', function() {
|
|||
balance.lockedAmount.should.equal(helpers.toSatoshi(4));
|
||||
balance.totalKbToSendMax.should.equal(2);
|
||||
var max = (balance.totalAmount - balance.lockedAmount) - (balance.totalKbToSendMax * 2000);
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0, 2000);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0, 2000);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1774,7 +1809,7 @@ describe('Wallet service', function() {
|
|||
|
||||
function(next) {
|
||||
async.each(_.range(3), function(i, next) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
server.rejectTx({
|
||||
|
@ -1787,7 +1822,7 @@ describe('Wallet service', function() {
|
|||
},
|
||||
function(next) {
|
||||
// Allow a 4th tx
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
server.rejectTx({
|
||||
txProposalId: tx.id,
|
||||
|
@ -1797,7 +1832,7 @@ describe('Wallet service', function() {
|
|||
},
|
||||
function(next) {
|
||||
// Do not allow before backoff time
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('NOTALLOWEDTOCREATETX');
|
||||
|
@ -1806,7 +1841,7 @@ describe('Wallet service', function() {
|
|||
},
|
||||
function(next) {
|
||||
var clock = sinon.useFakeTimers(Date.now() + (WalletService.backoffTimeMinutes + 2) * 60 * 1000, 'Date');
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
clock.restore();
|
||||
server.rejectTx({
|
||||
|
@ -1818,7 +1853,7 @@ describe('Wallet service', function() {
|
|||
function(next) {
|
||||
// Do not allow a 5th tx before backoff time
|
||||
var clock = sinon.useFakeTimers(Date.now() + (WalletService.backoffTimeMinutes + 2) * 60 * 1000 + 1, 'Date');
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 1, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
clock.restore();
|
||||
should.exist(err);
|
||||
|
@ -1841,7 +1876,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -1931,7 +1966,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
@ -2136,7 +2171,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, [10, 10], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp);
|
||||
|
@ -2195,7 +2230,7 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should fail to brodcast a not yet accepted tx', function(done) {
|
||||
helpers.stubBroadcast('999');
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some other message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some other message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp);
|
||||
|
@ -2288,7 +2323,7 @@ describe('Wallet service', function() {
|
|||
});
|
||||
|
||||
it('other copayers should see pending proposal created by one copayer', function(done) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp);
|
||||
|
@ -2309,7 +2344,7 @@ describe('Wallet service', function() {
|
|||
async.waterfall([
|
||||
|
||||
function(next) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
txpId = txp.id;
|
||||
should.not.exist(err);
|
||||
|
@ -2397,7 +2432,7 @@ describe('Wallet service', function() {
|
|||
async.waterfall([
|
||||
|
||||
function(next) {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
txpId = txp.id;
|
||||
should.not.exist(err);
|
||||
|
@ -2479,7 +2514,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, 10, function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 9, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp);
|
||||
|
@ -2535,11 +2570,12 @@ describe('Wallet service', function() {
|
|||
helpers.createAndJoinWallet(1, 1, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, _.range(10), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, null, TestData.copayers[0].privKey_1H_0);
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 11), function() {
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, null, TestData.copayers[0].privKey_1H_0);
|
||||
async.eachSeries(_.range(10), function(i, next) {
|
||||
clock.tick(10 * 1000);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
|
@ -2560,7 +2596,7 @@ describe('Wallet service', function() {
|
|||
}, function(err, txps) {
|
||||
should.not.exist(err);
|
||||
var times = _.pluck(txps, 'createdOn');
|
||||
times.should.deep.equal([90, 80, 70, 60]);
|
||||
times.should.deep.equal([100, 90, 80, 70, 60]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -2583,7 +2619,7 @@ describe('Wallet service', function() {
|
|||
}, function(err, txps) {
|
||||
should.not.exist(err);
|
||||
var times = _.pluck(txps, 'createdOn');
|
||||
times.should.deep.equal([90, 80, 70, 60]);
|
||||
times.should.deep.equal([100, 90, 80, 70]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -2592,7 +2628,7 @@ describe('Wallet service', function() {
|
|||
server.getTxs({}, function(err, txps) {
|
||||
should.not.exist(err);
|
||||
var times = _.pluck(txps, 'createdOn');
|
||||
times.should.deep.equal([90, 80, 70, 60, 50, 40, 30, 20, 10]);
|
||||
times.should.deep.equal([100, 90, 80, 70, 60, 50, 40, 30, 20, 10]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -2620,7 +2656,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, _.range(4), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, null, TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, null, TestData.copayers[0].privKey_1H_0);
|
||||
async.eachSeries(_.range(3), function(i, next) {
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
|
@ -2832,7 +2868,7 @@ describe('Wallet service', function() {
|
|||
wallet2 = w;
|
||||
|
||||
helpers.stubUtxos(server2, wallet2, _.range(1, 3), function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, 'some message', TestData.copayers[1].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, 'some message', TestData.copayers[1].privKey_1H_0);
|
||||
async.eachSeries(_.range(2), function(i, next) {
|
||||
server2.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
|
@ -2898,7 +2934,7 @@ describe('Wallet service', function() {
|
|||
server = s;
|
||||
wallet = w;
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function() {
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
txp = txs[0];
|
||||
|
@ -3210,7 +3246,7 @@ describe('Wallet service', function() {
|
|||
server._normalizeTxHistory = sinon.stub().returnsArg(0);
|
||||
|
||||
helpers.stubUtxos(server, wallet, [100, 200], function(utxos) {
|
||||
var txOpts = helpers.createProposalOpts(mainAddresses[0].address, 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
var txOpts = helpers.createSimpleProposalOpts(mainAddresses[0].address, 80, 'some message', TestData.copayers[0].privKey_1H_0);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
|
|
|
@ -4,21 +4,21 @@ var _ = require('lodash');
|
|||
var chai = require('chai');
|
||||
var sinon = require('sinon');
|
||||
var should = chai.should();
|
||||
var TXP = require('../../lib/model/txproposal');
|
||||
var TxProposal = require('../../lib/model/txproposal');
|
||||
var Bitcore = require('bitcore-wallet-utils').Bitcore;
|
||||
var WalletUtils = require('bitcore-wallet-utils');
|
||||
|
||||
describe('TXProposal', function() {
|
||||
|
||||
describe('#create', function() {
|
||||
it('should create a TXP', function() {
|
||||
var txp = TXP.create(aTxpOpts());
|
||||
it('should create a TxProposal', function() {
|
||||
var txp = TxProposal.create(aTxpOpts());
|
||||
should.exist(txp);
|
||||
should.exist(txp.toAddress);
|
||||
should.not.exist(txp.outputs);
|
||||
});
|
||||
it('should create a multiple-outputs TXP', function() {
|
||||
var txp = TXP.create(aTxpOpts(TXP.Types.MULTIPLEOUTPUTS));
|
||||
it('should create a multiple-outputs TxProposal', function() {
|
||||
var txp = TxProposal.create(aTxpOpts(TxProposal.Types.MULTIPLEOUTPUTS));
|
||||
should.exist(txp);
|
||||
should.not.exist(txp.toAddress);
|
||||
should.exist(txp.outputs);
|
||||
|
@ -26,26 +26,26 @@ describe('TXProposal', function() {
|
|||
});
|
||||
|
||||
describe('#fromObj', function() {
|
||||
it('should copy a TXP', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
it('should copy a TxProposal', function() {
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
should.exist(txp);
|
||||
txp.toAddress.should.equal(aTXP().toAddress);
|
||||
});
|
||||
it('should copy a multiple-outputs TXP', function() {
|
||||
var txp = TXP.fromObj(aTXP(TXP.Types.MULTIPLEOUTPUTS));
|
||||
it('should copy a multiple-outputs TxProposal', function() {
|
||||
var txp = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS));
|
||||
should.exist(txp);
|
||||
txp.outputs.should.deep.equal(aTXP(TXP.Types.MULTIPLEOUTPUTS).outputs);
|
||||
txp.outputs.should.deep.equal(aTXP(TxProposal.Types.MULTIPLEOUTPUTS).outputs);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getBitcoreTx', function() {
|
||||
it('should create a valid bitcore TX', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
var t = txp.getBitcoreTx();
|
||||
should.exist(t);
|
||||
});
|
||||
it('should order outputs as specified by outputOrder', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
|
||||
txp.outputOrder = [0, 1];
|
||||
var t = txp.getBitcoreTx();
|
||||
|
@ -56,47 +56,32 @@ describe('TXProposal', function() {
|
|||
t.getChangeOutput().should.deep.equal(t.outputs[0]);
|
||||
});
|
||||
it('should create a bitcore TX with multiple outputs', function() {
|
||||
var txp = TXP.fromObj(aTXP(TXP.Types.MULTIPLEOUTPUTS));
|
||||
var txp = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS));
|
||||
txp.outputOrder = [0, 1, 2];
|
||||
var t = txp.getBitcoreTx();
|
||||
t.getChangeOutput().should.deep.equal(t.outputs[2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getHeader', function() {
|
||||
it('should be compatible with simple proposal legacy header', function() {
|
||||
var x = TXP.fromObj(aTXP());
|
||||
var proposalHeader = x.getHeader();
|
||||
var pH = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader);
|
||||
var uH = WalletUtils.getProposalHash(x.toAddress, x.amount, x.message, x.payProUrl);
|
||||
pH.should.equal(uH);
|
||||
});
|
||||
it('should handle multiple-outputs', function() {
|
||||
var x = TXP.fromObj(aTXP(TXP.Types.MULTIPLEOUTPUTS));
|
||||
var proposalHeader = x.getHeader();
|
||||
should.exist(proposalHeader);
|
||||
var pH = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader);
|
||||
should.exist(pH);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getTotalAmount', function() {
|
||||
it('should be compatible with simple proposal legacy amount', function() {
|
||||
var x = TXP.fromObj(aTXP());
|
||||
var x = TxProposal.fromObj(aTXP());
|
||||
var total = x.getTotalAmount();
|
||||
total.should.equal(x.amount);
|
||||
});
|
||||
it('should handle multiple-outputs', function() {
|
||||
var x = TXP.fromObj(aTXP(TXP.Types.MULTIPLEOUTPUTS));
|
||||
var x = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS));
|
||||
var totalOutput = 0;
|
||||
_.each(x.outputs, function(o) { totalOutput += o.amount });
|
||||
_.each(x.outputs, function(o) {
|
||||
totalOutput += o.amount
|
||||
});
|
||||
x.getTotalAmount().should.equal(totalOutput);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#sign', function() {
|
||||
it('should sign 2-2', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
txp.sign('1', theSignatures, theXPub);
|
||||
txp.isAccepted().should.equal(false);
|
||||
txp.isRejected().should.equal(false);
|
||||
|
@ -108,7 +93,7 @@ describe('TXProposal', function() {
|
|||
|
||||
describe('#getRawTx', function() {
|
||||
it('should generate correct raw transaction for signed 2-2', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
txp.sign('1', theSignatures, theXPub);
|
||||
txp.getRawTx().should.equal('0100000001ab069f7073be9b491bb1ad4233a45d2e383082ccc7206df905662d6d8499e66e080000009200483045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9014752210319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f2103b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d10130152aeffffffff0280f0fa02000000001976a91451224bca38efcaa31d5340917c3f3f713b8b20e488ac70c9fa020000000017a914778192003f0e9e1d865c082179cc3dae5464b03d8700000000');
|
||||
});
|
||||
|
@ -118,7 +103,7 @@ describe('TXProposal', function() {
|
|||
|
||||
describe('#reject', function() {
|
||||
it('should reject 2-2', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
txp.reject('1');
|
||||
txp.isAccepted().should.equal(false);
|
||||
txp.isRejected().should.equal(true);
|
||||
|
@ -128,7 +113,7 @@ describe('TXProposal', function() {
|
|||
|
||||
describe('#reject & #sign', function() {
|
||||
it('should finally reject', function() {
|
||||
var txp = TXP.fromObj(aTXP());
|
||||
var txp = TxProposal.fromObj(aTXP());
|
||||
txp.sign('1', theSignatures);
|
||||
txp.isAccepted().should.equal(false);
|
||||
txp.isRejected().should.equal(false);
|
||||
|
@ -151,19 +136,16 @@ var aTxpOpts = function(type) {
|
|||
amount: 50000000,
|
||||
message: 'some message'
|
||||
};
|
||||
if (type == TXP.Types.MULTIPLEOUTPUTS) {
|
||||
opts.outputs = [
|
||||
{
|
||||
if (type == TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
opts.outputs = [{
|
||||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7",
|
||||
amount: 10000000,
|
||||
message: "first message"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7",
|
||||
amount: 20000000,
|
||||
message: "second message"
|
||||
},
|
||||
];
|
||||
}, ];
|
||||
delete opts.toAddress;
|
||||
delete opts.amount;
|
||||
}
|
||||
|
@ -171,8 +153,18 @@ var aTxpOpts = function(type) {
|
|||
};
|
||||
|
||||
var aTXP = function(type) {
|
||||
var version;
|
||||
switch (type) {
|
||||
case TxProposal.Types.MULTIPLEOUTPUTS:
|
||||
version = '1.0.1';
|
||||
break;
|
||||
default:
|
||||
version = '1.0.0';
|
||||
break
|
||||
}
|
||||
|
||||
var txp = {
|
||||
"version": "1.0.0",
|
||||
"version": version,
|
||||
"type": type,
|
||||
"createdOn": 1423146231,
|
||||
"id": "75c34f49-1ed6-255f-e9fd-0c71ae75ed1e",
|
||||
|
@ -209,19 +201,16 @@ var aTXP = function(type) {
|
|||
"actions": [],
|
||||
"outputOrder": [0, 1],
|
||||
};
|
||||
if (type == TXP.Types.MULTIPLEOUTPUTS) {
|
||||
txp.outputs = [
|
||||
{
|
||||
if (type == TxProposal.Types.MULTIPLEOUTPUTS) {
|
||||
txp.outputs = [{
|
||||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7",
|
||||
amount: 10000000,
|
||||
message: "first message"
|
||||
},
|
||||
{
|
||||
}, {
|
||||
toAddress: "18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7",
|
||||
amount: 20000000,
|
||||
message: "second message"
|
||||
},
|
||||
];
|
||||
}, ];
|
||||
txp.outputOrder = [0, 1, 2];
|
||||
delete txp.toAddress;
|
||||
delete txp.amount;
|
||||
|
|
Loading…
Reference in New Issue