From aa8e5488009dde3b277a183b11821d6d288a8772 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Fri, 14 Aug 2015 11:26:17 -0700 Subject: [PATCH 1/3] To be effective within the current Bitcoin network, the inputs to a transaction with an nLocktime must be not the standard max value. We set the sequence number of 0 if the value is max. Currently sequence numbers other than MAX_INT32 have no meaning in the Bitcoin protocol but this may change in future BIPS --- lib/transaction/input/input.js | 2 ++ lib/transaction/transaction.js | 15 +++++++++++++++ test/transaction/transaction.js | 30 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/lib/transaction/input/input.js b/lib/transaction/input/input.js index c34afb521..d425768f5 100644 --- a/lib/transaction/input/input.js +++ b/lib/transaction/input/input.js @@ -13,6 +13,7 @@ var Output = require('../output'); var DEFAULT_SEQNUMBER = 0xFFFFFFFF; +var DEFAULT_LOCKTIME_SEQNUMBER = 0x00000000; function Input(params) { if (!(this instanceof Input)) { @@ -24,6 +25,7 @@ function Input(params) { } Input.DEFAULT_SEQNUMBER = DEFAULT_SEQNUMBER; +Input.DEFAULT_LOCKTIME_SEQNUMBER = DEFAULT_LOCKTIME_SEQNUMBER; Object.defineProperty(Input.prototype, 'script', { configurable: false, diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index 2c200318a..c106bcd04 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -414,6 +414,13 @@ Transaction.prototype.lockUntilDate = function(time) { if (_.isDate(time)) { time = time.getTime() / 1000; } + + for (var i = 0; i < this.inputs.length; i++) { + if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER){ + this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER; + } + } + this.nLockTime = time; return this; }; @@ -433,6 +440,14 @@ Transaction.prototype.lockUntilBlockHeight = function(height) { if (height < 0) { throw new errors.Transaction.NLockTimeOutOfRange(); } + + for (var i = 0; i < this.inputs.length; i++) { + if (this.inputs[i].sequenceNumber === Input.DEFAULT_SEQNUMBER){ + this.inputs[i].sequenceNumber = Input.DEFAULT_LOCKTIME_SEQNUMBER; + } + } + + this.nLockTime = height; return this; }; diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 2cee2221f..0746af967 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -748,6 +748,36 @@ describe('Transaction', function() { return new Transaction().lockUntilBlockHeight(-1); }).to.throw(errors.Transaction.NLockTimeOutOfRange); }); + it('has a non-max sequenceNumber for effective date locktime tx', function() { + var transaction = new Transaction() + .from(simpleUtxoWith1BTC) + .lockUntilDate(date); + expect(transaction.inputs[0].sequenceNumber + .should.not.equal(Transaction.Input.DEFAULT_SEQNUMBER)); + }); + it('has a non-max sequenceNumber for effective blockheight locktime tx', function() { + var transaction = new Transaction() + .from(simpleUtxoWith1BTC) + .lockUntilBlockHeight(blockHeight); + expect(transaction.inputs[0].sequenceNumber + .should.not.equal(Transaction.Input.DEFAULT_SEQNUMBER)); + }); + it('should serialize correctly for date locktime ', function() { + var transaction= new Transaction() + .from(simpleUtxoWith1BTC) + .lockUntilDate(date); + var serialized_tx = transaction.uncheckedSerialize(); + var copy = new Transaction(serialized_tx); + expect(serialized_tx.should.equal(copy.uncheckedSerialize())); + }); + it('should serialize correctly for a block height locktime', function() { + var transaction= new Transaction() + .from(simpleUtxoWith1BTC) + .lockUntilBlockHeight(blockHeight); + var serialized_tx = transaction.uncheckedSerialize(); + var copy = new Transaction(serialized_tx); + expect(serialized_tx.should.equal(copy.uncheckedSerialize())); + }); }); it('handles anyone-can-spend utxo', function() { From c7c1122123e930e2a13d9a953f3bd57f1eb972c2 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Thu, 27 Aug 2015 14:54:42 -0700 Subject: [PATCH 2/3] Adjusted test cases as requested. Test that the SequenceNumber is zero Remove unncessary expect --- test/transaction/transaction.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 0746af967..5b1a498ab 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -752,15 +752,15 @@ describe('Transaction', function() { var transaction = new Transaction() .from(simpleUtxoWith1BTC) .lockUntilDate(date); - expect(transaction.inputs[0].sequenceNumber - .should.not.equal(Transaction.Input.DEFAULT_SEQNUMBER)); + transaction.inputs[0].sequenceNumber + .should.equal(Transaction.Input.DEFAULT_LOCKTIME_SEQNUMBER); }); it('has a non-max sequenceNumber for effective blockheight locktime tx', function() { var transaction = new Transaction() .from(simpleUtxoWith1BTC) .lockUntilBlockHeight(blockHeight); - expect(transaction.inputs[0].sequenceNumber - .should.not.equal(Transaction.Input.DEFAULT_SEQNUMBER)); + transaction.inputs[0].sequenceNumber + .should.equal(Transaction.Input.DEFAULT_LOCKTIME_SEQNUMBER); }); it('should serialize correctly for date locktime ', function() { var transaction= new Transaction() @@ -768,7 +768,7 @@ describe('Transaction', function() { .lockUntilDate(date); var serialized_tx = transaction.uncheckedSerialize(); var copy = new Transaction(serialized_tx); - expect(serialized_tx.should.equal(copy.uncheckedSerialize())); + serialized_tx.should.equal(copy.uncheckedSerialize()); }); it('should serialize correctly for a block height locktime', function() { var transaction= new Transaction() @@ -776,7 +776,7 @@ describe('Transaction', function() { .lockUntilBlockHeight(blockHeight); var serialized_tx = transaction.uncheckedSerialize(); var copy = new Transaction(serialized_tx); - expect(serialized_tx.should.equal(copy.uncheckedSerialize())); + serialized_tx.should.equal(copy.uncheckedSerialize()); }); }); From c9980dc9b2c8f3ae1604ad8d492a3142cb1988b0 Mon Sep 17 00:00:00 2001 From: Zaki Manian Date: Thu, 27 Aug 2015 15:46:22 -0700 Subject: [PATCH 3/3] Ensure that sequence number is preserved on after serialize->deserialize --- test/transaction/transaction.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 5b1a498ab..fbbc5abed 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -769,6 +769,8 @@ describe('Transaction', function() { var serialized_tx = transaction.uncheckedSerialize(); var copy = new Transaction(serialized_tx); serialized_tx.should.equal(copy.uncheckedSerialize()); + copy.inputs[0].sequenceNumber + .should.equal(Transaction.Input.DEFAULT_LOCKTIME_SEQNUMBER) }); it('should serialize correctly for a block height locktime', function() { var transaction= new Transaction() @@ -777,6 +779,8 @@ describe('Transaction', function() { var serialized_tx = transaction.uncheckedSerialize(); var copy = new Transaction(serialized_tx); serialized_tx.should.equal(copy.uncheckedSerialize()); + copy.inputs[0].sequenceNumber + .should.equal(Transaction.Input.DEFAULT_LOCKTIME_SEQNUMBER) }); });