<p>I believe you are correct, both about the fact that height-based logic came first and time was added later, and the fact that this is a hard-forking change. A transaction with a locktime > 500000000 would not have been acceptable before the change, but possibly valid after.</p>

<p>Of course, given that no such transactions occurred (to the best of my knowledge), it isn't exactly a fork; but it was a change that could have led to one.</p>

<p>Also, before the removal of OP_VER, technically speaking every new release had a hard forking change of this nature.</p>

CC BY-SA 4.0
2019-09-10T18:06:36.637Z
2019-09-10T21:41:49.673Z
2019-09-10T21:41:49.673Z
Pieter Wuille
<p>The Wiki was incorrect (I wrote the original text and I apologize).  As indicated in the question, the ordering on the Wiki was backwards.  However, upon investigating, I don't believe it was a hard fork.  In the original Bitcoin 0.1 code, we find the following <a href="https://github.com/trottier/original-bitcoin/commit/4184ab26345d19e87045ce7d9291e60e7d36e096#diff-e8db9b851adc2422aadfffca88f14c91R398-R401" rel="noreferrer">function</a>:</p>
<pre><code>bool IsFinal() const
{
    if (nLockTime == 0 || nLockTime &lt; nBestHeight)
        return true;
    ...
}
</code></pre>
<p>This function is called in the mining <a href="https://github.com/trottier/original-bitcoin/commit/4184ab26345d19e87045ce7d9291e60e7d36e096#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2246-R2247" rel="noreferrer">code</a> that selects which transactions to mine:</p>
<pre><code>if (tx.IsCoinBase() || !tx.IsFinal())
      continue;
</code></pre>
<p>That means no node running the default Bitcoin 0.1 code would mine a transaction unless its nLockTime was less than the next block's height.  However, I don't see any code in 0.1 that rejects blocks with a &quot;non-final&quot; transaction, so this isn't a consensus rule.</p>
<p>Months later, in code that became part of Bitcoin 0.1.6, the timelock feature is <a href="https://github.com/bitcoin/bitcoin/commit/cc0b4c3b62367a2aebe5fc1f4d0ed4b97e9c2ac9#diff-23cfe05393c8433e384d2c385f06ab93R404-R413" rel="noreferrer">added</a>:</p>
<pre><code>-    bool IsFinal() const
+    bool IsFinal(int64 nBlockTime=0) const
     {
-        if (nLockTime == 0 || nLockTime &lt; nBestHeight)
+        // Time based nLockTime implemented in 0.1.6,
+        // do not use time based until most 0.1.5 nodes have upgraded.
+        if (nBlockTime == 0)
+            nBlockTime = GetAdjustedTime();
+        if (nLockTime == 0)
+            return true;
+        if (nLockTime &lt; (nLockTime &lt; 500000000 ? nBestHeight : nBlockTime))
</code></pre>
<p>And the same commit also <a href="https://github.com/bitcoin/bitcoin/commit/cc0b4c3b62367a2aebe5fc1f4d0ed4b97e9c2ac9#diff-118fcbaaba162ba17933c7893247df3aR1221-R1225" rel="noreferrer">adds</a> consensus enforcement for both heightlocks and timelocks:</p>
<pre><code>+    // Check that all transactions are finalized (starting around 30 Nov 2009)
+    if (nBestHeight &gt; 31000) // 25620 + 5320
+        foreach(const CTransaction&amp; tx, vtx)
+            if (!tx.IsFinal(nTime))
+                return error(&quot;AcceptBlock() : contains a non-final transaction&quot;);
</code></pre>
<p>In short, in version 0.1.0 had no consensus rules about the nLockTime field and version 0.1.6 added a consensus rule that included both heightlocks and timelocks.  That's making the rules more strict and so I believe it was a soft fork which activated at block 31,000.</p>
<p>Perhaps of interest, this is the earliest soft fork of which I'm aware (as of this writing, it's earlier than any entries at the time of writing on the Wiki's <a href="https://en.bitcoin.it/wiki/Consensus_versions" rel="noreferrer">consensus versions</a> list or BitMex's <a href="https://blog.bitmex.com/bitcoins-consensus-forks/" rel="noreferrer">consensus forks</a> list).  Despite being so early, the fork was obviously made with care, giving the network about a month to upgrade and <a href="https://github.com/bitcoin/bitcoin/commit/dd519206a684c772a4a06ceecc87c665ad09d8be#diff-118fcbaaba162ba17933c7893247df3aR418-R420" rel="noreferrer">preparing</a> for potential problems ahead of time.</p>

CC BY-SA 4.0
2020-09-18T20:05:48.117Z
2020-09-18T20:42:59.007Z
2020-09-18T20:42:59.007Z
David A. Harding
<p>Currently, the <a href="https://en.bitcoin.it/wiki/Timelock" rel="nofollow noreferrer">Bitcoin Wiki</a> states:</p>

<blockquote>
  <p>A part of the original Bitcoin implementation, nLockTime is a field that specifies the earliest time a transaction may be added to a valid block. A later Bitcoin soft fork allowed nLockTime to alternatively specify the lowest block height a transaction may be added to a valid block.</p>
</blockquote>

<p>Is the ordering correct? It appears that the source code as of <a href="https://github.com/bitcoin/bitcoin/blob/8dca7864f793072701f810e4c5ea12a6e1087085/main.h#L399" rel="nofollow noreferrer">v0.1.5</a> used the block height and <a href="https://github.com/bitcoin/bitcoin/blob/956468c29adcbff83ab7c2e52d91f8e87b704853/main.h#L401" rel="nofollow noreferrer">v0.1.6</a> extended this to handle timestamps. Also, does this new ordering make it a hard-fork?</p>

<bitcoin-core><script><locktime>
nLockTime in Bitcoin Core
Martin Harrigan
# nLockTime in Bitcoin Core

(Asked by: username_0 on 2019-09-04)

Currently, the [Bitcoin Wiki](https://en.bitcoin.it/wiki/Timelock) states:

> A part of the original Bitcoin implementation, nLockTime is a field that specifies the earliest time a transaction may be added to a valid block. A later Bitcoin soft fork allowed nLockTime to alternatively specify the lowest block height a transaction may be added to a valid block.

Is the ordering correct? It appears that the source code as of [v0.1.5](https://github.com/bitcoin/bitcoin/blob/8dca7864f793072701f810e4c5ea12a6e1087085/main.h#L399) used the block height and [v0.1.6](https://github.com/bitcoin/bitcoin/blob/956468c29adcbff83ab7c2e52d91f8e87b704853/main.h#L401) extended this to handle timestamps. Also, does this new ordering make it a hard-fork?

**Tags:** bitcoin-core, script, locktime


---

## 2 answers

### Answer by username_2 on 2020-09-18. Score: 8 | Accepted answer

The Wiki was incorrect (I wrote the original text and I apologize). As indicated in the question, the ordering on the Wiki was backwards. However, upon investigating, I don't believe it was a hard fork. In the original Bitcoin 0.1 code, we find the following [function](https://github.com/trottier/original-bitcoin/commit/4184ab26345d19e87045ce7d9291e60e7d36e096#diff-e8db9b851adc2422aadfffca88f14c91R398-R401):

```
bool IsFinal() const
{
    if (nLockTime == 0 || nLockTime < nBestHeight)
        return true;
    ...
}

```

This function is called in the mining [code](https://github.com/trottier/original-bitcoin/commit/4184ab26345d19e87045ce7d9291e60e7d36e096#diff-7ec3c68a81efff79b6ca22ac1f1eabbaR2246-R2247) that selects which transactions to mine:

```
if (tx.IsCoinBase() || !tx.IsFinal())
      continue;

```

That means no node running the default Bitcoin 0.1 code would mine a transaction unless its nLockTime was less than the next block's height. However, I don't see any code in 0.1 that rejects blocks with a "non-final" transaction, so this isn't a consensus rule.

Months later, in code that became part of Bitcoin 0.1.6, the timelock feature is [added](https://github.com/bitcoin/bitcoin/commit/cc0b4c3b62367a2aebe5fc1f4d0ed4b97e9c2ac9#diff-23cfe05393c8433e384d2c385f06ab93R404-R413):

```
-    bool IsFinal() const
+    bool IsFinal(int64 nBlockTime=0) const
     {
-        if (nLockTime == 0 || nLockTime < nBestHeight)
+        // Time based nLockTime implemented in 0.1.6,
+        // do not use time based until most 0.1.5 nodes have upgraded.
+        if (nBlockTime == 0)
+            nBlockTime = GetAdjustedTime();
+        if (nLockTime == 0)
+            return true;
+        if (nLockTime < (nLockTime < 500000000 ? nBestHeight : nBlockTime))

```

And the same commit also [adds](https://github.com/bitcoin/bitcoin/commit/cc0b4c3b62367a2aebe5fc1f4d0ed4b97e9c2ac9#diff-118fcbaaba162ba17933c7893247df3aR1221-R1225) consensus enforcement for both heightlocks and timelocks:

```
+    // Check that all transactions are finalized (starting around 30 Nov 2009)
+    if (nBestHeight > 31000) // 25620 + 5320
+        foreach(const CTransaction& tx, vtx)
+            if (!tx.IsFinal(nTime))
+                return error("AcceptBlock() : contains a non-final transaction");

```

In short, in version 0.1.0 had no consensus rules about the nLockTime field and version 0.1.6 added a consensus rule that included both heightlocks and timelocks. That's making the rules more strict and so I believe it was a soft fork which activated at block 31,000.

Perhaps of interest, this is the earliest soft fork of which I'm aware (as of this writing, it's earlier than any entries at the time of writing on the Wiki's [consensus versions](https://en.bitcoin.it/wiki/Consensus_versions) list or BitMex's [consensus forks](https://blog.bitmex.com/bitcoins-consensus-forks/) list). Despite being so early, the fork was obviously made with care, giving the network about a month to upgrade and [preparing](https://github.com/bitcoin/bitcoin/commit/dd519206a684c772a4a06ceecc87c665ad09d8be#diff-118fcbaaba162ba17933c7893247df3aR418-R420) for potential problems ahead of time.


---

### Answer by username_1 on 2019-09-10. Score: 10

I believe you are correct, both about the fact that height-based logic came first and time was added later, and the fact that this is a hard-forking change. A transaction with a locktime > 500000000 would not have been acceptable before the change, but possibly valid after.

Of course, given that no such transactions occurred (to the best of my knowledge), it isn't exactly a fork; but it was a change that could have led to one.

Also, before the removal of OP\_VER, technically speaking every new release had a hard forking change of this nature.


---

