Lock time and sequence numbers
A Transaction’s nLockTime stops miners from including it before a given block height (values below
500,000,000) or Unix time in seconds (values from 500,000,000). It turns out that nodes enforce it only if at
least one Input has a sequence number below 0xFFFFFFFF. When all the Inputs are final, the lock time is
ignored. See the transaction format for both fields.
The examples use the keys and the fundingTransaction helper described in About the examples. This page’s examples share the following setup:
final myAddress = aliceKey.toAddress(networkType: NetworkType.TEST);final fundingTx = fundingTransaction(P2PKHLockBuilder.fromAddress(myAddress).getScriptPubkey(), BigInt.from(100000));final signer = DefaultTransactionSigner(sighashAll, aliceKey);Until a block height
Section titled “Until a block height”final tx = TransactionBuilder() .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToPKH(myAddress, BigInt.from(99900)) .lockUntilBlockHeight(900000) // after the Inputs, since it lowers their sequence numbers .build(true);
tx.nLockTime; // 900000tx.inputs[0].sequenceNumber; // 0xFFFFFFFE, so the lock time is enforcedlockUntilBlockHeight lowers the sequence numbers of the Inputs already added to 0xFFFFFFFE, so we call it
after the spendFrom… calls:
final tx = TransactionBuilder() .lockUntilBlockHeight(900000) // no Inputs yet, so no sequence number is changed .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToPKH(myAddress, BigInt.from(99900)) .build(true);
tx.inputs[0].sequenceNumber; // 0xFFFFFFFF: every Input is final, so nodes ignore nLockTimeUntil a time
Section titled “Until a time”We give lockUntilUnixTime the seconds since the epoch. It does not change sequence numbers, so we pass a
non-final sequence number when adding the Input:
final unlockAt = DateTime.utc(2030, 1, 1).millisecondsSinceEpoch ~/ 1000; // seconds
final tx = TransactionBuilder() // lockUntilUnixTime does not touch sequence numbers, so we pass a non-final one ourselves. .spendFromTxnWithSigner(signer, fundingTx, 0, TransactionInput.MAX_SEQ_NUMBER - 1, P2PKHUnlockBuilder(aliceKey.publicKey)) .spendToPKH(myAddress, BigInt.from(99900)) .lockUntilUnixTime(unlockAt) .build(true);
tx.nLockTime; // 1893456000Sequence numbers
Section titled “Sequence numbers”Every spendFrom… method takes the Input’s sequence number. TransactionInput.MAX_SEQ_NUMBER (0xFFFFFFFF)
marks a final Input, and anything lower makes the Input non-final. We can also set
transaction.inputs[i].sequenceNumber before signing.
Reading the lock time
Section titled “Reading the lock time”final tx = Transaction.fromHex(raw);
final lockTime = tx.nLockTime;final isBlockHeight = lockTime < Transaction.LOCKTIME_THRESHOLD; // below 500000000: a block heightfinal enforced = tx.inputs.any((input) => input.sequenceNumber != TransactionInput.MAX_SEQ_NUMBER);
