user120470: 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?

user101498: 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
OP_IF
    # If so, anyone that can provide a signature for it can spend this coin.
    OP_CHECKSIG
OP_ELSE
    # Otherwise, are we presented a 32-bytes long bytestring?
    <remote_htlcpubkey> OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_IF
        # 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
    OP_ELSE
        # Otherwise, we assume this is the timeout path.
        # We must not be able to spend through this path before `cltv_expiry` blocks.
        OP_DROP <cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP
        # A signature against the remote HTLC pubkey is enough to spend after the timelock.
        OP_CHECKSIG
    OP_ENDIF
OP_ENDIF

(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.)


120470: <p>BOLT #3 <a href="https://github.com/lightning/bolts/blob/master/03-transactions.md#htlc-timeout-and-htlc-success-transactions" rel="nofollow noreferrer">specifies</a> that an HTLC success transaction (the second-stage transaction that spends an HTLC by providing the preimage) has the witness script <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt;  &lt;payment_preimage&gt;</code>.
<a href="https://bitcoin.stackexchange.com/questions/92087/can-someone-explain-how-an-htlc-is-redeemed">This question</a> and the respective answers assume that the input script of the HTLC success transaction is <code>&lt;remotehtlcsig&gt; &lt;payment_preimage&gt;</code>. The answers explain in detail how the scripts are executed.</p>
<p>As <a href="https://github.com/ElementsProject/lightning/blob/master/bitcoin/script.c#L805" rel="nofollow noreferrer">implementations seem</a> 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 <code>localhtlcsig</code>? Is the <code>localhtlcsig</code> dropped from the stack at some point during the execution?</p>
 101498: <p>At the present time the Script for a &quot;received HTLC output&quot;, that is for an HTLC that is paying us materialized as an output in our own commitment transaction, is (comments mine):</p>
<pre><code># Is it the hash of the revocation public key that we are presented?
OP_DUP OP_HASH160 &lt;RIPEMD160(SHA256(revocationpubkey))&gt; OP_EQUAL
OP_IF
    # If so, anyone that can provide a signature for it can spend this coin.
    OP_CHECKSIG
OP_ELSE
    # Otherwise, are we presented a 32-bytes long bytestring?
    &lt;remote_htlcpubkey&gt; OP_SWAP OP_SIZE 32 OP_EQUAL
    OP_IF
        # 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 &lt;RIPEMD160(payment_hash)&gt; 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 &lt;local_htlcpubkey&gt; 2 OP_CHECKMULTISIG
    OP_ELSE
        # Otherwise, we assume this is the timeout path.
        # We must not be able to spend through this path before `cltv_expiry` blocks.
        OP_DROP &lt;cltv_expiry&gt; OP_CHECKLOCKTIMEVERIFY OP_DROP
        # A signature against the remote HTLC pubkey is enough to spend after the timelock.
        OP_CHECKSIG
    OP_ENDIF
OP_ENDIF
</code></pre>
<p><em>(I omitted <code>option_anchor_outputs</code> for simplicity and since it doesn't matter for this question.)</em></p>
<p>If this output ends up onchain and we know the <code>sha256()</code> preimage of <code>payment_hash</code>, 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 <a href="https://bitcoin.stackexchange.com/a/97225/101498">this</a> and <a href="https://bitcoin.stackexchange.com/a/95543/101498">this</a> answers for more about 2-stage HTLCs).</p>
<p>Here is the detailed script execution when spending using the HTLC-success transaction:</p>
<ul>
<li>Initial stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt;  &lt;payment_preimage&gt;</code></li>
<li>Executing <code>OP_DUP</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt;  &lt;payment_preimage&gt; &lt;payment_preimage&gt;</code>.</li>
<li>Executing <code>OP_HASH160 &lt;RIPEMD160(SHA256(revocationpubkey))&gt; OP_EQUAL</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;payment_preimage&gt; 0</code>.</li>
<li>Executing <code>OP_HASH160 &lt;RIPEMD160(SHA256(revocationpubkey))&gt; OP_EQUAL</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;payment_preimage&gt; 0</code>.</li>
<li>Executing <code>OP_IF</code>, we &quot;jump&quot; in the <code>ELSE</code> branch of the conditional. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;payment_preimage&gt;</code>.</li>
<li>Executing <code>&lt;remote_htlcpubkey&gt; OP_SWAP</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;remote_htlcpubkey&gt; &lt;payment_preimage&gt;</code>.</li>
<li>Executing <code>OP_SIZE 32 OP_EQUAL</code>. Resulting stack (since <code>payment_preimage</code> is 32 bytes long): <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;remote_htlcpubkey&gt; &lt;payment_preimage&gt; 1</code>.</li>
<li>Executing <code>OP_IF</code>, we &quot;jump&quot; in the first branch of the conditional. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;remote_htlcpubkey&gt; &lt;payment_preimage&gt;</code>.</li>
<li>Executing <code>OP_HASH160 &lt;RIPEMD160(payment_hash)&gt; OP_EQUALVERIFY</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; &lt;remote_htlcpubkey&gt;</code>.</li>
<li>Executing <code>2 OP_SWAP &lt;local_htlcpubkey&gt; 2</code>. Resulting stack: <code>0 &lt;remotehtlcsig&gt; &lt;localhtlcsig&gt; 2 &lt;remote_htlcpubkey&gt; &lt;local_htlcpubkey&gt; 2</code>.</li>
<li>Executing <code>OP_CHECKMULTISIG</code>. Resulting stack: <code>1</code>. (Do keep in mind the <code>CHECKMULTISIG</code> bug that makes it consume one-too-many elements on the stack.)</li>
</ul>
