check available utxos when sending temporary txp
This commit is contained in:
parent
30c8072b8b
commit
8ef05c8841
|
@ -26,6 +26,7 @@ var errors = {
|
||||||
TX_NOT_ACCEPTED: 'The transaction proposal is not accepted',
|
TX_NOT_ACCEPTED: 'The transaction proposal is not accepted',
|
||||||
TX_NOT_FOUND: 'Transaction proposal not found',
|
TX_NOT_FOUND: 'Transaction proposal not found',
|
||||||
TX_NOT_PENDING: 'The transaction proposal is not pending',
|
TX_NOT_PENDING: 'The transaction proposal is not pending',
|
||||||
|
UNAVAILABLE_UTXOS: 'Unavailable unspent outputs',
|
||||||
UPGRADE_NEEDED: 'Client app needs to be upgraded',
|
UPGRADE_NEEDED: 'Client app needs to be upgraded',
|
||||||
WALLET_ALREADY_EXISTS: 'Wallet already exists',
|
WALLET_ALREADY_EXISTS: 'Wallet already exists',
|
||||||
WALLET_FULL: 'Wallet full',
|
WALLET_FULL: 'Wallet full',
|
||||||
|
|
|
@ -1436,6 +1436,10 @@ WalletService.prototype.createTx2 = function(opts, cb) {
|
||||||
WalletService.prototype.sendTx = function(opts, cb) {
|
WalletService.prototype.sendTx = function(opts, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
function utxoKey(utxo) {
|
||||||
|
return utxo.txid + '|' + utxo.vout
|
||||||
|
};
|
||||||
|
|
||||||
if (!Utils.checkRequired(opts, ['txProposalId', 'proposalSignature', 'proposalSignaturePubKey', 'proposalSignaturePubKeySig']))
|
if (!Utils.checkRequired(opts, ['txProposalId', 'proposalSignature', 'proposalSignaturePubKey', 'proposalSignaturePubKeySig']))
|
||||||
return cb(new ClientError('Required argument missing'));
|
return cb(new ClientError('Required argument missing'));
|
||||||
|
|
||||||
|
@ -1445,10 +1449,23 @@ WalletService.prototype.sendTx = function(opts, cb) {
|
||||||
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
if (!txp) return cb(Errors.TX_NOT_FOUND);
|
||||||
if (!txp.isTemporary()) return cb();
|
if (!txp.isTemporary()) return cb();
|
||||||
|
|
||||||
txp.status = 'pending';
|
// Verify UTXOs are still available
|
||||||
self.storage.storeTx(self.walletId, txp, function(err) {
|
self.getUtxos({}, function(err, utxos) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
return cb();
|
|
||||||
|
var txpInputs = _.map(txp.inputs, utxoKey);
|
||||||
|
var lockedUtxoIndex = _.indexBy(_.filter(utxos, 'locked'), utxoKey);
|
||||||
|
var unavailable = _.any(txpInputs, function(i) {
|
||||||
|
return lockedUtxoIndex[i];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (unavailable) return cb(Errors.UNAVAILABLE_UTXOS);
|
||||||
|
|
||||||
|
txp.status = 'pending';
|
||||||
|
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
return cb();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2409,6 +2409,84 @@ describe('Wallet service', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
it('should fail to send a temporary tx proposal if utxos are unavailable', function(done) {
|
||||||
|
var txp1, txp2;
|
||||||
|
var txOpts = helpers.createProposalOpts2([{
|
||||||
|
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||||
|
amount: 0.8
|
||||||
|
}], {
|
||||||
|
message: 'some message',
|
||||||
|
});
|
||||||
|
|
||||||
|
async.waterfall([
|
||||||
|
|
||||||
|
function(next) {
|
||||||
|
helpers.stubUtxos(server, wallet, [1, 2], function() {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
server.createTx2(txOpts, next);
|
||||||
|
},
|
||||||
|
function(txp, next) {
|
||||||
|
txp1 = txp;
|
||||||
|
server.createTx2(txOpts, next);
|
||||||
|
},
|
||||||
|
function(txp, next) {
|
||||||
|
txp2 = txp;
|
||||||
|
should.exist(txp1);
|
||||||
|
should.exist(txp2);
|
||||||
|
server.sendTx({
|
||||||
|
txProposalId: txp1.id,
|
||||||
|
proposalSignature: 'dummy',
|
||||||
|
proposalSignaturePubKey: 'dummy',
|
||||||
|
proposalSignaturePubKeySig: 'dummy',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
server.sendTx({
|
||||||
|
txProposalId: txp2.id,
|
||||||
|
proposalSignature: 'dummy',
|
||||||
|
proposalSignaturePubKey: 'dummy',
|
||||||
|
proposalSignaturePubKeySig: 'dummy',
|
||||||
|
}, function(err) {
|
||||||
|
should.exist(err);
|
||||||
|
err.code.should.equal('UNAVAILABLE_UTXOS');
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
should.not.exist(err);
|
||||||
|
txs.length.should.equal(1);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
// A new tx proposal should use the next available UTXO
|
||||||
|
server.createTx2(txOpts, next);
|
||||||
|
},
|
||||||
|
function(txp3, next) {
|
||||||
|
should.exist(txp3);
|
||||||
|
server.sendTx({
|
||||||
|
txProposalId: txp3.id,
|
||||||
|
proposalSignature: 'dummy',
|
||||||
|
proposalSignaturePubKey: 'dummy',
|
||||||
|
proposalSignaturePubKeySig: 'dummy',
|
||||||
|
}, next);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
should.not.exist(err);
|
||||||
|
txs.length.should.equal(2);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
], function(err) {
|
||||||
|
should.not.exist(err);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
describe('#createTx backoff time', function(done) {
|
describe('#createTx backoff time', function(done) {
|
||||||
var server, wallet, txid;
|
var server, wallet, txid;
|
||||||
|
|
Loading…
Reference in New Issue