Browse Source

backoff time only active after backoffOffset

activeAddress
Matias Alejo Garcia 10 years ago
parent
commit
4569f1d3c5
  1. 17
      lib/server.js
  2. 119
      test/integration/server.js

17
lib/server.js

@ -53,8 +53,11 @@ function WalletService() {
// Time after which a Tx proposal can be erased by any copayer. in seconds // Time after which a Tx proposal can be erased by any copayer. in seconds
WalletService.deleteLockTime = 24 * 3600; WalletService.deleteLockTime = 24 * 3600;
// Time a copayer need to wait to create a new TX after her tx previous proposal we rejected. (incremental). in seconds. // Allowed consecutive txp rejections before backoff is applied.
WalletService.backoffTime = 2 * 60; WalletService.backoffOffset = 3;
// Time a copayer need to wait to create a new TX after her tx previous proposal we rejected. (incremental). in Minutes.
WalletService.backoffTimeMinutes = 2;
// Fund scanning parameters // Fund scanning parameters
WalletService.scanConfig = { WalletService.scanConfig = {
@ -753,15 +756,19 @@ WalletService.prototype._canCreateTx = function(copayerId, cb) {
if (!txs.length) if (!txs.length)
return cb(null, true); return cb(null, true);
var lastRejections = _.takeWhile(txs, {status: 'rejected'}); var lastRejections = _.takeWhile(txs, {
status: 'rejected'
});
if (!lastRejections.length) var exceededRejections = lastRejections.length - WalletService.backoffOffset;
if (exceededRejections <= 0)
return cb(null, true); return cb(null, true);
var lastTxTs = txs[0].createdOn; var lastTxTs = txs[0].createdOn;
var now = Math.floor(Date.now() / 1000); var now = Math.floor(Date.now() / 1000);
var timeSinceLastRejection = now - lastTxTs; var timeSinceLastRejection = now - lastTxTs;
var backoffTime = Math.pow(WalletService.backoffTime,lastRejections.length); var backoffTime = 60 * Math.pow(WalletService.backoffTimeMinutes, exceededRejections);
if (timeSinceLastRejection <= backoffTime) if (timeSinceLastRejection <= backoffTime)
log.debug('Not allowing to create TX: timeSinceLastRejection/backoffTime', timeSinceLastRejection, backoffTime); log.debug('Not allowing to create TX: timeSinceLastRejection/backoffTime', timeSinceLastRejection, backoffTime);

119
test/integration/server.js

@ -1675,43 +1675,67 @@ describe('Wallet service', function() {
}); });
describe('#createTx backoff time', function() { describe('#createTx backoff time', function() {
var server, wallet, txid; var server, wallet;
beforeEach(function(done) { beforeEach(function(done) {
helpers.createAndJoinWallet(2, 2, function(s, w) { helpers.createAndJoinWallet(2, 2, function(s, w) {
server = s; server = s;
wallet = w; wallet = w;
done();
});
});
it('should allow to create inmediatly after a 3 rejections', function(done) {
async.series([
function(next) {
async.each([0, 1, 2], function(i, a_next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
server.createTx(txOpts, function(err, tx) { server.createTx(txOpts, function(err, tx) {
should.not.exist(err); should.not.exist(err);
should.exist(tx); server.rejectTx({
txid = tx.id; txProposalId: tx.id,
done(); reason: 'some reason',
}, function(err) {
should.not.exist(err);
a_next();
}); });
}); });
}); });
}, next);
},
function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
next();
});
});
}
], done);
}); });
it('should fail to create inmediatly after a rejection', function(done) { it('should NOT allow to create inmediatly after a 4 rejections', function(done) {
async.series([ async.series([
function(next) { function(next) {
server.getPendingTxs({}, function(err, txs) { async.each([0, 1, 2, 3], function(i, a_next) {
var tx = txs[0]; helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
tx.id.should.equal(txid); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
next(); server.createTx(txOpts, function(err, tx) {
}); should.not.exist(err);
},
function(next) {
server.rejectTx({ server.rejectTx({
txProposalId: txid, txProposalId: tx.id,
reason: 'some reason', reason: 'some reason',
}, function(err) { }, function(err) {
should.not.exist(err); should.not.exist(err);
next(); a_next();
});
});
}); });
}, next);
}, },
function(next) { function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
@ -1725,24 +1749,25 @@ describe('Wallet service', function() {
], done); ], done);
}); });
it('should allow to create after backoffTime', function(done) { it('should allow to create inmediatly after a 4 rejections after backofftime', function(done) {
async.series([ async.series([
function(next) { function(next) {
server.getPendingTxs({}, function(err, txs) { async.each([0, 1, 2, 3], function(i, a_next) {
var tx = txs[0]; helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
tx.id.should.equal(txid); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
next(); server.createTx(txOpts, function(err, tx) {
}); should.not.exist(err);
},
function(next) {
server.rejectTx({ server.rejectTx({
txProposalId: txid, txProposalId: tx.id,
reason: 'some reason', reason: 'some reason',
}, function(err) { }, function(err) {
should.not.exist(err); should.not.exist(err);
next(); a_next();
}); });
});
});
}, next);
}, },
function(next) { function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
@ -1756,64 +1781,59 @@ describe('Wallet service', function() {
function(next) { function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
var clock = sinon.useFakeTimers(Date.now() + WalletService.backoffTimeMinutes * 60 * 1000 + 2000);
var clock = sinon.useFakeTimers(Date.now() + 2000 + WalletService.backoffTime * 1000);
server.createTx(txOpts, function(err, tx) { server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
clock.restore(); clock.restore();
should.not.exist(err);
next(); next();
}); });
}); });
}, },
], done); ], done);
}); });
it('should not allow to create after backoffTime and 2 rejections', function(done) {
it('should NOT allow to create after a 5 rejections after backofftime', function(done) {
async.series([ async.series([
function(next) { function(next) {
async.each([0, 1, 2, 3, 4], function(i, a_next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
server.createTx(txOpts, function(err, tx) { server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
next();
});
});
},
function(next) {
server.getPendingTxs({}, function(err, tx) {
should.not.exist(err); should.not.exist(err);
server.rejectTx({ server.rejectTx({
txProposalId: tx[0].id, txProposalId: tx.id,
reason: 'some reason', reason: 'some reason',
}, function(err) { }, function(err) {
should.not.exist(err); should.not.exist(err);
server.rejectTx({ a_next();
txProposalId: tx[1].id,
reason: 'some other reason',
}, function(err) {
should.not.exist(err);
next();
}); });
}); });
}); });
}, next);
}, },
function(next) { function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() { helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0); var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
var clock = sinon.useFakeTimers(Date.now() + 2000 + WalletService.backoffTime * 1000);
server.createTx(txOpts, function(err, tx) { server.createTx(txOpts, function(err, tx) {
err.code.should.equal('NOTALLOWEDTOCREATETX'); next();
});
});
},
function(next) {
helpers.stubUtxos(server, wallet, _.range(1, 9), function() {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey_1H_0);
var clock = sinon.useFakeTimers(Date.now() + WalletService.backoffTimeMinutes * 60 * 1000 + 2000);
server.createTx(txOpts, function(err, tx) {
clock.restore(); clock.restore();
err.code.should.equal('NOTALLOWEDTOCREATETX');
next(); next();
}); });
}); });
}, },
], done); ], done);
}); });
}); });
@ -2437,6 +2457,7 @@ describe('Wallet service', function() {
next(); next();
}); });
}, function(err) { }, function(err) {
clock.restore();
return done(err); return done(err);
}); });
}); });

Loading…
Cancel
Save