TeachMeBitcoin

Lock funds until block 850,000

From TeachMeBitcoin, the free encyclopedia Reading time: 4 min

3. Absolute Timelocks Using CLTV in Real Scripts

What Is an Absolute Timelock?

An absolute timelock specifies a fixed, immovable point in time (or block height) before which funds cannot be spent. Think of it like a safe that's programmed to open only after a specific calendar date — regardless of when the safe was closed or who tries to open it. CLTV implements absolute timelocks in Bitcoin Script.

Pattern 1: Simple Delayed Payment

The most basic CLTV use case is a time-delayed payment to a single recipient.

# Lock funds until block 850,000
scriptPubKey (P2SH inner script / redeemScript):
  OP_IF
    850000 OP_CHECKLOCKTIMEVERIFY OP_DROP
    OP_DUP OP_HASH160 <RecipientPubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
  OP_ELSE
    OP_DUP OP_HASH160 <SenderPubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
  OP_ENDIF

In this script, the recipient can only spend after block 850,000. Before that, the sender can reclaim the funds. This pattern is useful for scheduled payments or revocable commitments.

Pattern 2: Two-Party Absolute Timelock

For more security, you might require a 2-of-2 multisig before the timelock expires, and a single signature after:

# Before expiry: requires both Alice and Bob
# After expiry: Alice can spend alone
OP_IF
  <expiry_block> OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <AlicePubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
OP_ELSE
  OP_2 <AlicePubKey> <BobPubKey> OP_2 OP_CHECKMULTISIG
OP_ENDIF

This is a common escrow pattern where two parties must agree until a deadline, after which one party (Alice) can unilaterally reclaim funds.

Pattern 3: P2SH-Wrapped CLTV Script

In practice, CLTV scripts are usually wrapped in Pay-to-Script-Hash (P2SH) to keep the scriptPubKey short and hide the contract logic until spending time.

# Step 1: Define the redeemScript
redeemScript:
  <lock_until_block> OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <RecipientPubKeyHash>
  OP_EQUALVERIFY OP_CHECKSIG

# Step 2: Compute P2SH address
p2sh_address = Base58Check(OP_HASH160 HASH160(redeemScript) OP_EQUAL)

# Step 3: Locking transaction output
scriptPubKey: OP_HASH160 <hash160_of_redeemScript> OP_EQUAL

# Step 4: Spending transaction input
scriptSig: <signature> <pubKey> <redeemScript>

The beauty of P2SH wrapping is that the sender just sends to a normal-looking address. The timelock logic is only revealed when the funds are spent.

Pattern 4: P2WSH (SegWit) CLTV Script

Modern Bitcoin transactions use Segregated Witness for CLTV scripts, especially in Lightning contexts.

# Witness script (equivalent to redeemScript in SegWit)
witnessScript:
  <lock_time> OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <PubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

# scriptPubKey
scriptPubKey: OP_0 <sha256(witnessScript)>

# Witness stack when spending:
witness:
  <signature>
  <pubKey>
  <witnessScript>

Real-World Calculation: Setting CLTV Values

When encoding a CLTV value into a script, you use Bitcoin's Script integer encoding (little-endian, sign-magnitude). Here's how you'd encode a block height of 800,000 in Python:

def encode_script_int(value):
    """Encode an integer as a Bitcoin Script pushdata"""
    if value == 0:
        return b''

    result = []
    neg = value < 0
    absvalue = abs(value)

    while absvalue:
        result.append(absvalue & 0xff)
        absvalue >>= 8

    if result[-1] & 0x80:
        result.append(0x80 if neg else 0x00)
    elif neg:
        result[-1] |= 0x80

    return bytes(result)

# Encode block height 800000
lock_height = 800_000
encoded = encode_script_int(lock_height)
print(encoded.hex())  # Output: 804012 (3 bytes)

# The script push opcode for 3 bytes is 0x03
# Full script fragment: 03 80 40 12 b1 (CLTV opcode is 0xb1)

Pattern 5: Multi-Recipient with Staggered Timelocks

A more complex pattern allows different recipients access at different times — useful for vesting schedules or inheritance ladders.

# Year 1: Bob gets 25% (block ~700000)
# Year 2: Bob gets 50% (block ~752500)
# Year 3: Bob gets 100% (block ~805000)
# At any time: Alice + Bob can spend together (2-of-2)

# This requires splitting the UTXO into multiple outputs:
# Output 1 (25% of funds):
  700000 OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <BobPubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

# Output 2 (25% of funds):
  752500 OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <BobPubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

# Output 3 (50% of funds):
  805000 OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <BobPubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

Limitations of Absolute Timelocks

Absolute timelocks are powerful but have a key limitation: the lock date is fixed at script creation time. If you create a CLTV script that expires at block 900,000 and the current block is 850,000, that's fine. But if blocks are mined faster than expected (which doesn't happen in Bitcoin due to difficulty adjustment, but in forks it might), the lock expires earlier in wall-clock time. Conversely, if you want a lock that expires "6 months from when a channel is opened," a fixed block height won't work — you need CSV (relative timelocks) for that.

☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!