diff --git a/docs/transaction.md b/docs/transaction.md index a669eca..29b794d 100644 --- a/docs/transaction.md +++ b/docs/transaction.md @@ -140,6 +140,26 @@ var multiSigTx = new Transaction(serialized) assert(multiSigTx.isFullySigned()); ``` +## Time-Locking transaction +All bitcoin transactions contain a locktime field. +The locktime indicates the earliest time a transaction can be added to the blockchain. +Locktime allows signers to create time-locked transactions which will only become valid in the future, giving the signers a chance to change their minds. +Locktime can be set in the form of a bitcoin block height (the transaction can only be included in a block with a higher height than specified) or a linux timestamp (transaction can only be confirmed after that time). +For more information see [bitcoin's development guide section on locktime](https://bitcoin.org/en/developer-guide#locktime-and-sequence-number). + +In bitcore, you can set a `Transaction`'s locktime by using the methods `Transaction#lockUntilDate` and `Transaction#lockUntilBlockHeight`. You can also get a friendly version of the locktime field via `Transaction#getLockTime`; + +For example: +```javascript +var future = new Date(2025,10,30); // Sun Nov 30 2025 +var transaction = new Transaction() + .lockUntilDate(future); +console.log(transaction.getLockTime()); +// output similar to: Sun Nov 30 2025 00:00:00 GMT-0300 (ART) +``` + + + ## Upcoming changes We're debating an API for Merge Avoidance, CoinJoin, Smart contracts, CoinSwap, and Stealth Addresses. We're expecting to have all of them by some time in 2015. Payment channel creation is avaliable in the [bitcore-channel](https://github.com/bitpay/bitcore-channel) module. diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index 09e7250..e0edb54 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -323,6 +323,23 @@ Transaction.prototype.lockUntilBlockHeight = function(height) { return this; }; +/** + * Returns a semantic version of the transaction's nLockTime. + * @return {Number|Date} + * If nLockTime is 0, it returns null, + * if it is < 500000000, it returns a block height (number) + * else it returns a Date object. + */ +Transaction.prototype.getLockTime = function() { + if (!this.nLockTime) { + return null; + } + if (this.nLockTime < Transaction.NLOCKTIME_BLOCKHEIGHT_LIMIT) { + return this.nLockTime; + } + return new Date(1000*this.nLockTime); +}; + Transaction.prototype.toJSON = function toJSON() { return JSON.stringify(this.toObject()); }; diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index db0e81f..c54571b 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -135,7 +135,7 @@ describe('Transaction', function() { describe('adding inputs', function() { - it('it only adds once one utxo', function() { + it('only adds once one utxo', function() { var tx = new Transaction(); tx.from(simpleUtxoWith1BTC); tx.from(simpleUtxoWith1BTC); @@ -408,25 +408,35 @@ describe('Transaction', function() { }); }); - describe('setting the nLockTime', function() { + describe('handling the nLockTime', function() { var MILLIS_IN_SECOND = 1000; var timestamp = 1423504946; var blockHeight = 342734; var date = new Date(timestamp * MILLIS_IN_SECOND); + it('handles a simple example', function() { + var future = new Date(2025,10,30); // Sun Nov 30 2025 + var transaction = new Transaction() + .lockUntilDate(future); + transaction.nLockTime.should.equal(future.getTime()/1000); + transaction.getLockTime().should.deep.equal(future); + }); it('accepts a date instance', function() { var transaction = new Transaction() .lockUntilDate(date); transaction.nLockTime.should.equal(timestamp); + transaction.getLockTime().should.deep.equal(date); }); it('accepts a number instance with a timestamp', function() { var transaction = new Transaction() .lockUntilDate(timestamp); transaction.nLockTime.should.equal(timestamp); + transaction.getLockTime().should.deep.equal(new Date(timestamp*1000)); }); it('accepts a block height', function() { var transaction = new Transaction() .lockUntilBlockHeight(blockHeight); transaction.nLockTime.should.equal(blockHeight); + transaction.getLockTime().should.deep.equal(blockHeight); }); it('fails if the block height is too high', function() { expect(function() { @@ -441,7 +451,7 @@ describe('Transaction', function() { return new Transaction().lockUntilDate(499999999); }).to.throw(errors.Transaction.LockTimeTooEarly); }); - it('fails if the date is negative', function() { + it('fails if the block height is negative', function() { expect(function() { return new Transaction().lockUntilBlockHeight(-1); }).to.throw(errors.Transaction.NLockTimeOutOfRange);