BOLT #3 specifies that an HTLC success transaction (the second-stage transaction that spends an HTLC by providing the preimage) has the witness script 0 <remotehtlcsig> <localhtlcsig> <payment_preimage>. This question and the respective answers assume that the input script of the HTLC success transaction is <remotehtlcsig> <payment_preimage>. The answers explain in detail how the scripts are executed.

As implementations seem to implement the script according to the specification, I wonder how the script is actually executed. How does the script execution work if there is the additional localhtlcsig? Is the localhtlcsig dropped from the stack at some point during the execution?

1 Answer 1


At the present time the Script for a "received HTLC output", that is for an HTLC that is paying us materialized as an output in our own commitment transaction, is (comments mine):

# Is it the hash of the revocation public key that we are presented?
OP_DUP OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL
    # If so, anyone that can provide a signature for it can spend this coin.
    # Otherwise, are we presented a 32-bytes long bytestring?
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
        # If so, we assume this is the payment hash preimage.
        # It must hash through ripemd160(sha256()) to the ripemd160() of the payment hash (because the payment hash is itself the sha256() of the payment preimage).
        OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY
        # Now the payment preimage is valid verify we've got a signature from both the local and the remote node to spend this coin.
        2 OP_SWAP <local_htlcpubkey> 2 OP_CHECKMULTISIG
        # Otherwise, we assume this is the timeout path.
        # We must not be able to spend through this path before `cltv_expiry` blocks.
        # A signature against the remote HTLC pubkey is enough to spend after the timelock.

(I omitted option_anchor_outputs for simplicity and since it doesn't matter for this question.)

If this output ends up onchain and we know the sha256() preimage of payment_hash, we can spend it using the corresponding HTLC-success transaction. We also cannot spend it another way, to make sure the revocation delay is enforced (see this and this answers for more about 2-stage HTLCs).

Here is the detailed script execution when spending using the HTLC-success transaction:

  • Initial stack: 0 <remotehtlcsig> <localhtlcsig> <payment_preimage>
  • Executing OP_DUP. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <payment_preimage> <payment_preimage>.
  • Executing OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <payment_preimage> 0.
  • Executing OP_HASH160 <RIPEMD160(SHA256(revocationpubkey))> OP_EQUAL. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <payment_preimage> 0.
  • Executing OP_IF, we "jump" in the ELSE branch of the conditional. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <payment_preimage>.
  • Executing <remote_htlcpubkey> OP_SWAP. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <remote_htlcpubkey> <payment_preimage>.
  • Executing OP_SIZE 32 OP_EQUAL. Resulting stack (since payment_preimage is 32 bytes long): 0 <remotehtlcsig> <localhtlcsig> <remote_htlcpubkey> <payment_preimage> 1.
  • Executing OP_IF, we "jump" in the first branch of the conditional. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <remote_htlcpubkey> <payment_preimage>.
  • Executing OP_HASH160 <RIPEMD160(payment_hash)> OP_EQUALVERIFY. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> <remote_htlcpubkey>.
  • Executing 2 OP_SWAP <local_htlcpubkey> 2. Resulting stack: 0 <remotehtlcsig> <localhtlcsig> 2 <remote_htlcpubkey> <local_htlcpubkey> 2.
  • Executing OP_CHECKMULTISIG. Resulting stack: 1. (Do keep in mind the CHECKMULTISIG bug that makes it consume one-too-many elements on the stack.)
  • Thank you! I see. I had overlooked the fact that the second-stage HTLC success transactions are for "received HTLC outputs" while the second-stage HTLC timeout transactions are for "offered HTLC outputs".
    – matthias
    Feb 27 at 14:53
  • You're welcome. I've added comments to the Script as well to (hopefully!) give more clarity about what it's doing. Feb 27 at 14:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.