Merge remote-tracking branch 'upstream/master' into feat/multOutputs
Conflicts: test/integration/server.js
This commit is contained in:
commit
40dc1c095b
|
@ -109,6 +109,10 @@ TxProposal.fromObj = function(obj) {
|
|||
return x;
|
||||
};
|
||||
|
||||
TxProposal.prototype.setInputs = function(inputs) {
|
||||
this.inputs = inputs;
|
||||
this.inputPaths = _.pluck(inputs, 'path');
|
||||
};
|
||||
|
||||
TxProposal.prototype._updateStatus = function() {
|
||||
if (this.status != 'pending') return;
|
||||
|
|
|
@ -602,7 +602,9 @@ WalletService.prototype._getUtxos = function(cb) {
|
|||
return cb(new ClientError('BLOCKCHAINERROR', 'Could not fetch unspent outputs'));
|
||||
}
|
||||
var utxos = _.map(inutxos, function(utxo) {
|
||||
return _.pick(utxo, ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis']);
|
||||
var u = _.pick(utxo, ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis']);
|
||||
u.locked = false;
|
||||
return u;
|
||||
});
|
||||
self.getPendingTxs({}, function(err, txps) {
|
||||
if (err) return cb(err);
|
||||
|
@ -657,6 +659,31 @@ WalletService.prototype._totalizeUtxos = function(utxos) {
|
|||
};
|
||||
|
||||
|
||||
WalletService.prototype._computeKbToSendMax = function(utxos, amount, cb) {
|
||||
var self = this;
|
||||
|
||||
var unlockedUtxos = _.filter(utxos, {
|
||||
locked: false
|
||||
});
|
||||
if (_.isEmpty(unlockedUtxos)) return cb(null, 0);
|
||||
|
||||
self.getWallet({}, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var t = WalletUtils.newBitcoreTransaction();
|
||||
try {
|
||||
_.each(unlockedUtxos, function(i) {
|
||||
t.from(i, i.publicKeys, wallet.m);
|
||||
});
|
||||
t.to(utxos[0].address, amount);
|
||||
var sizeInKb = Math.ceil(t._estimateSize() / 1000);
|
||||
return cb(null, sizeInKb);
|
||||
} catch (ex) {
|
||||
return cb(ex);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new transaction proposal.
|
||||
* @param {Object} opts
|
||||
|
@ -686,7 +713,13 @@ WalletService.prototype.getBalance = function(opts, cb) {
|
|||
|
||||
balance.byAddress = _.values(byAddress);
|
||||
|
||||
return cb(null, balance);
|
||||
self._computeKbToSendMax(utxos, balance.totalAmount - balance.lockedAmount, function(err, sizeInKb) {
|
||||
if (err) {
|
||||
log.error('Could not compute fees needed to transfer max amount', err);
|
||||
}
|
||||
balance.totalKbToSendMax = sizeInKb || 0;
|
||||
return cb(null, balance);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -720,13 +753,12 @@ WalletService.prototype._selectTxInputs = function(txp, cb) {
|
|||
|
||||
if (total >= txp.getTotalAmount()) {
|
||||
try {
|
||||
txp.inputs = selected;
|
||||
txp.setInputs(selected);
|
||||
bitcoreTx = txp.getBitcoreTx();
|
||||
bitcoreError = bitcoreTx.getSerializationError({
|
||||
disableIsFullySigned: true,
|
||||
});
|
||||
if (!bitcoreError) {
|
||||
txp.inputPaths = _.pluck(txp.inputs, 'path');
|
||||
txp.fee = bitcoreTx.getFee();
|
||||
return cb();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "bitcore-wallet-service",
|
||||
"description": "A service for Mutisig HD Bitcoin Wallets",
|
||||
"author": "BitPay Inc",
|
||||
"version": "0.0.35",
|
||||
"version": "0.0.36",
|
||||
"keywords": [
|
||||
"bitcoin",
|
||||
"copay",
|
||||
|
|
|
@ -1144,6 +1144,7 @@ describe('Wallet service', function() {
|
|||
should.exist(balance);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(6));
|
||||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalKbToSendMax.should.equal(1);
|
||||
should.exist(balance.byAddress);
|
||||
balance.byAddress.length.should.equal(2);
|
||||
balance.byAddress[0].amount.should.equal(helpers.toSatoshi(4));
|
||||
|
@ -1163,6 +1164,7 @@ describe('Wallet service', function() {
|
|||
should.exist(balance);
|
||||
balance.totalAmount.should.equal(0);
|
||||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalKbToSendMax.should.equal(0);
|
||||
should.exist(balance.byAddress);
|
||||
balance.byAddress.length.should.equal(0);
|
||||
done();
|
||||
|
@ -1177,6 +1179,7 @@ describe('Wallet service', function() {
|
|||
should.exist(balance);
|
||||
balance.totalAmount.should.equal(0);
|
||||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalKbToSendMax.should.equal(0);
|
||||
should.exist(balance.byAddress);
|
||||
balance.byAddress.length.should.equal(0);
|
||||
done();
|
||||
|
@ -1197,6 +1200,18 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
it('should return correct kb to send max', function(done) {
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 10, 0), function() {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
should.exist(balance);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(9));
|
||||
balance.lockedAmount.should.equal(0);
|
||||
balance.totalKbToSendMax.should.equal(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should fail gracefully when blockchain is unreachable', function(done) {
|
||||
blockchainExplorer.getUnspentUtxos = sinon.stub().callsArgWith(1, 'dummy error');
|
||||
server.createAddress({}, function(err, address) {
|
||||
|
@ -1688,6 +1703,57 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to send max amount', function(done) {
|
||||
helpers.stubUtxos(server, wallet, _.range(1, 10, 0), function() {
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(9));
|
||||
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);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
tx.amount.should.equal(max);
|
||||
tx.fee.should.equal(3 * 10000);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(9));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
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);
|
||||
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(9));
|
||||
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);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
should.exist(tx);
|
||||
tx.amount.should.equal(max);
|
||||
tx.fee.should.equal(2 * 2000);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(9));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createTx backoff time', function(done) {
|
||||
|
|
Loading…
Reference in New Issue