Merge pull request #306 from isocolsky/improve_fees

Support legacy clients
This commit is contained in:
Ivan Socolsky 2015-07-30 12:07:19 -03:00
commit 362d1a42bb
5 changed files with 181 additions and 26 deletions

View File

@ -9,7 +9,7 @@ var Address = Bitcore.Address;
var TxProposalAction = require('./txproposalaction');
function TxProposal() {
this.version = '1.0.1';
this.version = '2.0.0';
};
TxProposal.Types = {

View File

@ -865,6 +865,13 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
var inputs = sortUtxos(utxos);
var bitcoreTx, bitcoreError;
var serializationOpts = {
disableIsFullySigned: true,
};
if (!_.startsWith(txp.version, '1.')) {
serializationOpts.disableSmallFees = true;
serializationOpts.disableLargeFees = true;
}
while (i < inputs.length) {
selected.push(inputs[i]);
@ -876,11 +883,7 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
txp.setInputs(selected);
txp.estimateFee();
bitcoreTx = txp.getBitcoreTx();
bitcoreError = bitcoreTx.getSerializationError({
disableIsFullySigned: true,
disableSmallFees: true,
disableLargeFees: true,
});
bitcoreError = bitcoreTx.getSerializationError(serializationOpts);
if (!bitcoreError) {
txp.fee = bitcoreTx.getFee();
return cb();
@ -1052,6 +1055,10 @@ WalletService.prototype.createTx = function(opts, cb) {
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos,
});
if (!self.clientVersion || _.startsWith(self.clientVersion, 'bwc-0.0.')) {
txp.version = '1.0.1';
}
self._selectTxInputs(txp, function(err) {
if (err) return cb(err);
@ -1384,6 +1391,14 @@ WalletService.prototype.getPendingTxs = function(opts, cb) {
self.storage.fetchPendingTxs(self.walletId, function(err, txps) {
if (err) return cb(err);
if (_.startsWith(self.clientVersion, 'bwc-0.0.')) {
var allLegacy = _.all(txps, function(txp) {
return _.startsWith(txp.version, '1.');
});
if (!allLegacy) return cb(new Error('Some spend proposals were created using a newer version. Please upgrade your client app.'))
}
_.each(txps, function(txp) {
txp.deleteLockTime = self.getRemainingDeleteLockTime(txp);
});
@ -1534,7 +1549,12 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
amount = 0;
}
function outputMap(o) { return { amount: o.amount, address: o.address } };
function outputMap(o) {
return {
amount: o.amount,
address: o.address
}
};
var newTx = {
txid: tx.txid,
action: action,
@ -1542,7 +1562,9 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
fees: tx.fees,
time: tx.time,
addressTo: addressTo,
outputs: _.map(_.filter(outputs, { isChange: false }), outputMap),
outputs: _.map(_.filter(outputs, {
isChange: false
}), outputMap),
confirmations: tx.confirmations,
};
@ -1556,11 +1578,13 @@ WalletService.prototype.getTxHistory = function(opts, cb) {
return _.pick(action, ['createdOn', 'type', 'copayerId', 'copayerName', 'comment']);
});
_.each(newTx.outputs, function(output) {
var query = { toAddress: output.address, amount: output.amount };
var txpOut = _.find(proposal.outputs, query);
output.message = txpOut ? txpOut.message : null;
}
);
var query = {
toAddress: output.address,
amount: output.amount
};
var txpOut = _.find(proposal.outputs, query);
output.message = txpOut ? txpOut.message : null;
});
// newTx.sentTs = proposal.sentTs;
// newTx.merchant = proposal.merchant;
//newTx.paymentAckMemo = proposal.paymentAckMemo;

View File

@ -2,7 +2,7 @@
"name": "bitcore-wallet-service",
"description": "A service for Mutisig HD Bitcoin Wallets",
"author": "BitPay Inc",
"version": "0.0.47",
"version": "0.1.0",
"keywords": [
"bitcoin",
"copay",
@ -20,7 +20,7 @@
"dependencies": {
"async": "^0.9.0",
"bitcore": "^0.12.9",
"bitcore-wallet-utils": "^0.0.23",
"bitcore-wallet-utils": "^0.1.0",
"body-parser": "^1.11.0",
"coveralls": "^2.11.2",
"email-validator": "^1.0.1",

View File

@ -35,6 +35,7 @@ helpers.getAuthServer = function(copayerId, cb) {
copayerId: copayerId,
message: 'dummy',
signature: 'dummy',
clientVersion: 'bwc-0.1.0',
}, function(err, server) {
verifyStub.restore();
if (err || !server) throw new Error('Could not login as copayerId ' + copayerId);
@ -4249,4 +4250,144 @@ describe('Wallet service', function() {
});
});
});
describe('Legacy', function() {
describe('Fees', function() {
var server, wallet;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
done();
});
});
it('should create a tx from legacy (bwc-0.0.*) client', function(done) {
helpers.stubUtxos(server, wallet, [100, 200], function() {
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
var verifyStub = sinon.stub(WalletService.prototype, '_verifySignature');
verifyStub.returns(true);
WalletService.getInstanceWithAuth({
copayerId: wallet.copayers[0].id,
message: 'dummy',
signature: 'dummy',
clientVersion: 'bwc-0.0.40',
}, function(err, server) {
should.not.exist(err);
should.exist(server);
verifyStub.restore();
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
should.exist(tx);
tx.amount.should.equal(helpers.toSatoshi(80));
tx.fee.should.equal(WalletUtils.DEFAULT_FEE_PER_KB);
done();
});
});
});
});
it('should return error when fetching new txps from legacy (bwc-0.0.*) client', function(done) {
helpers.stubUtxos(server, wallet, [100, 200], function() {
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);
var verifyStub = sinon.stub(WalletService.prototype, '_verifySignature');
verifyStub.returns(true);
WalletService.getInstanceWithAuth({
copayerId: wallet.copayers[0].id,
message: 'dummy',
signature: 'dummy',
clientVersion: 'bwc-0.0.40',
}, function(err, server) {
should.not.exist(err);
should.exist(server);
verifyStub.restore();
server.getPendingTxs({}, function(err, txps) {
should.exist(err);
should.not.exist(txps);
err.toString().should.contain('created by a newer version');
done();
});
});
});
});
});
it('should create a tx from legacy (bwc-0.0.*) client and sign it from newer client', function(done) {
helpers.stubUtxos(server, wallet, [100, 200], function() {
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey_1H_0);
var verifyStub = sinon.stub(WalletService.prototype, '_verifySignature');
verifyStub.returns(true);
WalletService.getInstanceWithAuth({
copayerId: wallet.copayers[0].id,
message: 'dummy',
signature: 'dummy',
clientVersion: 'bwc-0.0.40',
}, function(err, server) {
should.not.exist(err);
should.exist(server);
verifyStub.restore();
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
should.exist(tx);
tx.amount.should.equal(helpers.toSatoshi(80));
tx.fee.should.equal(WalletUtils.DEFAULT_FEE_PER_KB);
helpers.getAuthServer(wallet.copayers[0].id, function(server) {
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err) {
should.not.exist(err);
done();
});
});
});
});
});
});
it('should fail with insufficient fee when invoked from legacy (bwc-0.0.*) client', function(done) {
helpers.stubUtxos(server, wallet, 1, function() {
var verifyStub = sinon.stub(WalletService.prototype, '_verifySignature');
verifyStub.returns(true);
WalletService.getInstanceWithAuth({
copayerId: wallet.copayers[0].id,
message: 'dummy',
signature: 'dummy',
clientVersion: 'bwc-0.0.40',
}, function(err, server) {
should.not.exist(err);
should.exist(server);
verifyStub.restore();
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.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);
// Sign it to make sure Bitcore doesn't complain about the fees
var signatures = helpers.clientSign(tx, TestData.copayers[0].xPrivKey);
server.signTx({
txProposalId: tx.id,
signatures: signatures,
}, function(err) {
should.not.exist(err);
done();
});
});
});
});
});
});
});
});
});

View File

@ -153,18 +153,8 @@ 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": version,
"version": '2.0.0',
"type": type,
"createdOn": 1423146231,
"id": "75c34f49-1ed6-255f-e9fd-0c71ae75ed1e",