<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="https://rusty.ozlabs.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://rusty.ozlabs.org/" rel="alternate" type="text/html" /><updated>2025-12-12T11:26:14+10:30</updated><id>https://rusty.ozlabs.org/feed.xml</id><title type="html">Rusty Russell’s Quiet Corner of The Internet</title><subtitle>I have been a full time Free Software developer since the 90&apos;s.  I worked on the Linux Kernel, where I wrote ipchains, iptables, and rewrote the module subsystem, along with many other things.  I changed careers in 2015 to work on Bitcoin full time, and was the first person to implement Lightning: I then chaired the Lightning Protocol Spec effort for several years, and that&apos;s my current job and hobby.</subtitle><entry><title type="html">CLN Developer Series #6: Neatening a Bugfix PR</title><link href="https://rusty.ozlabs.org/2025/12/12/CLN-Neatening-A-Bugfix-PR.html" rel="alternate" type="text/html" title="CLN Developer Series #6: Neatening a Bugfix PR" /><published>2025-12-12T00:00:00+10:30</published><updated>2025-12-12T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/12/CLN-Neatening-A-Bugfix-PR</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/12/CLN-Neatening-A-Bugfix-PR.html"><![CDATA[<p>This is an “eat your veggies!” talk, which is an indepth review of an <a href="https://github.com/ElementsProject/lightning/pull/8780">excellent PR</a> by @dovgopoly.  When someone first submits a PR, I like to explain every detail of how I would have done it, so they have some guidance about what the process looks like.</p>

<p>You can see <a href="https://github.com/rustyrussell/lightning/tree/pr-8780">the final result here</a>.</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-Neatening-A-Bug-Fix-PR.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-Neatening-A-Bug-Fix-PR.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[This is an “eat your veggies!” talk, which is an indepth review of an excellent PR by @dovgopoly. When someone first submits a PR, I like to explain every detail of how I would have done it, so they have some guidance about what the process looks like.]]></summary></entry><entry><title type="html">CLN Developer Series #5: Gossipd: The Gossip Daemon</title><link href="https://rusty.ozlabs.org/2025/12/09/CLN-Dev-Gossipd.html" rel="alternate" type="text/html" title="CLN Developer Series #5: Gossipd: The Gossip Daemon" /><published>2025-12-09T00:00:00+10:30</published><updated>2025-12-09T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/09/CLN-Dev-Gossipd</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/09/CLN-Dev-Gossipd.html"><![CDATA[<p>After the previous <a href="/2025/12/08/CLN-Dev-Finding-A-Gossip-Bug.html">aside on a gossip bug</a>, I realized I should do a tour of each daemon.  I started with gossipd because it’s my favorite, having changed so much from what it originally did into something which now mainly exports the “gossip_store” file for other subdaemons and plugins to use.</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-Gossipd.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-Gossipd.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[After the previous aside on a gossip bug, I realized I should do a tour of each daemon. I started with gossipd because it’s my favorite, having changed so much from what it originally did into something which now mainly exports the “gossip_store” file for other subdaemons and plugins to use.]]></summary></entry><entry><title type="html">CLN Developer Series #4: Finding A Gossip Bug</title><link href="https://rusty.ozlabs.org/2025/12/08/CLN-Dev-Finding-A-Gossip-Bug.html" rel="alternate" type="text/html" title="CLN Developer Series #4: Finding A Gossip Bug" /><published>2025-12-08T00:00:00+10:30</published><updated>2025-12-08T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/08/CLN-Dev-Finding-A-Gossip-Bug</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/08/CLN-Dev-Finding-A-Gossip-Bug.html"><![CDATA[<p>I stumbled over a bug while doing some work on gossipd, so I decided to record myself tracking it down.</p>

<p>I had reduced it to a simple test, and you can follow along from there.  Not sure how clear I was, since I didn’t know where this would go!  You can find the final <a href="https://github.com/ElementsProject/lightning/pull/8769]">pull request</a> on GitHub.</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-Finding-a-gossip-bug.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-Finding-a-gossip-bug.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[I stumbled over a bug while doing some work on gossipd, so I decided to record myself tracking it down.]]></summary></entry><entry><title type="html">CLN Developer Series #3: The Tal Heirarchical Allocator</title><link href="https://rusty.ozlabs.org/2025/12/04/CLN-Dev-3-Tal.html" rel="alternate" type="text/html" title="CLN Developer Series #3: The Tal Heirarchical Allocator" /><published>2025-12-04T00:00:00+10:30</published><updated>2025-12-04T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/04/CLN-Dev-3-Tal</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/04/CLN-Dev-3-Tal.html"><![CDATA[<p>This post is all about tal repostory, which is the CCAN module which I consider compulsory for any program which ever explicitly frees memory (you can write some very complex programs which don’t do that, FWIW!).</p>

<p>Understanding tal is a requirement for coding anything within CLN!</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-tal.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-tal.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[This post is all about tal repostory, which is the CCAN module which I consider compulsory for any program which ever explicitly frees memory (you can write some very complex programs which don’t do that, FWIW!).]]></summary></entry><entry><title type="html">CLN Developer Series: Overview of CLN Development</title><link href="https://rusty.ozlabs.org/2025/12/03/CLN-Dev-1.html" rel="alternate" type="text/html" title="CLN Developer Series: Overview of CLN Development" /><published>2025-12-03T00:00:00+10:30</published><updated>2025-12-03T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/03/CLN-Dev-1</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/03/CLN-Dev-1.html"><![CDATA[<p>This is the start of a one-per-workday video series, introducing Core Lightning Development to already-fluent C programmers.</p>

<p>The first video simply sets the groundwork of what the rest of the series should look like.</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-Overview.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-Overview.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[This is the start of a one-per-workday video series, introducing Core Lightning Development to already-fluent C programmers.]]></summary></entry><entry><title type="html">CLN Developer Series #2: The CCAN Utilities</title><link href="https://rusty.ozlabs.org/2025/12/03/CLN-Dev-2-CCAN.html" rel="alternate" type="text/html" title="CLN Developer Series #2: The CCAN Utilities" /><published>2025-12-03T00:00:00+10:30</published><updated>2025-12-03T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2025/12/03/CLN-Dev-2-CCAN</id><content type="html" xml:base="https://rusty.ozlabs.org/2025/12/03/CLN-Dev-2-CCAN.html"><![CDATA[<p>This post is all about the CCAN repostory, which contains all kinds of useful C routines which we use all over Core Lightning.  It lives, these days, on <a href="https://github.com/rustyrussell/ccan">GitHub</a>.</p>

<video width="420" height="315" controls="">
  <source src="https://rusty.ozlabs.org/video/CLN-ccan.mp4" type="video/mp4" />
  Your browser does not support the video tag.  Download from https://rusty.ozlabs.org/video/CLN-ccan.mp4
</video>]]></content><author><name></name></author><summary type="html"><![CDATA[This post is all about the CCAN repostory, which contains all kinds of useful C routines which we use all over Core Lightning. It lives, these days, on GitHub.]]></summary></entry><entry><title type="html">Restoring Bitcoin’s Full Script Power</title><link href="https://rusty.ozlabs.org/2024/01/19/the-great-opcode-restoration.html" rel="alternate" type="text/html" title="Restoring Bitcoin’s Full Script Power" /><published>2024-01-19T00:00:00+10:30</published><updated>2024-01-19T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2024/01/19/the-great-opcode-restoration</id><content type="html" xml:base="https://rusty.ozlabs.org/2024/01/19/the-great-opcode-restoration.html"><![CDATA[<p>In my previous posts I’ve been carefully considering what bitcoin Script improvements we might want if we had introspection.  Script was hobbled back in v0.3.1 due to denial-of-service issues: this has been a long-ongoing source of regret, but functions like <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> bring Script limitations into <a href="{ % post_url 2023-10-22-amounts-in-script}">clear focus</a>.</p>

<h3 id="ye-olde-bitcoin-script">Ye Olde Bitcoin Script</h3>

<p>Most people know that Satoshi disabled <code class="language-plaintext highlighter-rouge">OP_CAT</code> and a few other opcodes in v0.3.1, but Anthony Towns pointed out that until v0.3 bitcoin also allowed <a href="https://github.com/bitcoin/bitcoin/blob/v0.3.0/script.cpp#L44">arbitrary size numbers</a> using the OpenSSL BIGNUM type.</p>

<p>This was early in the project, and I completely understand the desire to avoid DoS immediately and clearly, and restore functionality later once the issues were carefully considered.  Unfortunately, the difficult nature of Script enhancements was not deeply appreciated until years later, so here we are!</p>

<h3 id="a-varops-budget-full-script-restoration-without-denial-of-service">A Varops Budget: Full Script Restoration Without Denial of Service</h3>

<p>BIP-342 replaced the global signature limit with a <a href="https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#resource-limits">sigops budget</a> based on weight, designed to be ample for any reasonable signature validation (such as might be produced by miniscript), yet limited enough to avoid denial of service.</p>

<p>We can use the approach for other operations whose expense is related to their operand size, and similarly remove existing arbitrary limits in script.  I call this a “varops” budget, as it applies to operations on variable-length operands.</p>

<p>My draft proposal sets the varops budget as simple:</p>
<ul>
  <li>The transaction weight multiplied by 520.</li>
</ul>

<p>This ensures that even if the budget were enforced on existing scripts, no script could conceivably fall short (e.g. each OP_SHA256 can always operate on the maximal-size stack object, with its own opcode weight supporting that budget).</p>

<p>Note: the budget is for the entire transaction, not per input: this is in anticipation of introspection opcodes which mean that a fairly short script may nonetheless want to examine other inputs which may be much larger.</p>

<p>The consumption of the various opcodes is as follows (anything not listed doesn’t have a cost):</p>

<table>
  <tbody>
    <tr>
      <td>Opcode</td>
      <td>Varops Budget Cost</td>
    </tr>
    <tr>
      <td>OP_CAT</td>
      <td>0</td>
    </tr>
    <tr>
      <td>OP_SUBSTR</td>
      <td>0</td>
    </tr>
    <tr>
      <td>OP_LEFT</td>
      <td>0</td>
    </tr>
    <tr>
      <td>OP_RIGHT</td>
      <td>0</td>
    </tr>
    <tr>
      <td>OP_INVERT</td>
      <td>1 + len(a) / 8</td>
    </tr>
    <tr>
      <td>OP_AND</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_OR</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_XOR</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_2MUL</td>
      <td>1 + len(a) / 8</td>
    </tr>
    <tr>
      <td>OP_2DIV</td>
      <td>1 + len(a) / 8</td>
    </tr>
    <tr>
      <td>OP_ADD</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_SUB</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_MUL</td>
      <td>(1 + len(a) / 8) * (1 + len(b) / 8</td>
    </tr>
    <tr>
      <td>OP_DIV</td>
      <td>(1 + len(a) / 8) * (1 + len(b) / 8</td>
    </tr>
    <tr>
      <td>OP_MOD</td>
      <td>(1 + len(a) / 8) * (1 + len(b) / 8</td>
    </tr>
    <tr>
      <td>OP_LSHIFT</td>
      <td>1 + len(a) / 8</td>
    </tr>
    <tr>
      <td>OP_RSHIFT</td>
      <td>1 + len(a) / 8</td>
    </tr>
    <tr>
      <td>OP_EQUAL</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_NOTEQUAL</td>
      <td>1 + MAX(len(a), len(b)) / 8</td>
    </tr>
    <tr>
      <td>OP_SHA256</td>
      <td>1 + len(a)</td>
    </tr>
    <tr>
      <td>OP_RIPEMD160</td>
      <td>0 (fails if len(a) &gt; 520 bytes)</td>
    </tr>
    <tr>
      <td>OP_SHA1</td>
      <td>0 (fails if len(a) &gt; 520 bytes)</td>
    </tr>
    <tr>
      <td>OP_HASH160</td>
      <td>1 + len(a)</td>
    </tr>
    <tr>
      <td>OP_HASH256</td>
      <td>1 + len(a)</td>
    </tr>
  </tbody>
</table>

<h3 id="removal-of-other-limits">Removal Of Other Limits</h3>

<p>Ethan Heilman’s <a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2023-October/022089.html">proposal for restoring OP_CAT</a> maintained a limit of 520 bytes for the result.  This can now be removed, in favor of a total stack limit already valid for taproot v1 (1000 elements and 520,000 bytes).</p>

<p>Further, if we were to introduce a new segwit version (such as Anthony Towns’ <a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-July/016249.html">generalized taproot</a>] or just to allow <a href="{ % post_url 2024-01-16-pay-to-tapscript.md}">keyless entry</a>, we can lift these limits to reasonable blocksize maxima (perhaps 10,000 elements totalling 4M bytes).</p>

<h3 id="minor-changes-to-semantics">Minor Changes to Semantics</h3>

<p>Values are still little-endian, but unsigned.  This simplifies implementation and makes the interaction of bit operations and arithmetic operations far simpler.  It allows existing positive numbers to use these opcodes without modification, not requiring conversion.</p>

<p>If a new segwit version were used, existing opcodes can be replaced, otherwise, new opcodes (e.g. <code class="language-plaintext highlighter-rouge">OP_ADDV</code>) would be added.</p>

<h3 id="implementation-details">Implementation Details</h3>

<p>The v0.3.0 implementation used a simple class wrapper of OpenSSL’s BIGNUM type, but for maximum clarity and simplicity I reimplemented each operation without external dependencies.</p>

<p>Except for <code class="language-plaintext highlighter-rouge">OP_EQUAL</code>/<code class="language-plaintext highlighter-rouge">OP_EQUALVERIFY</code>, each one converts to and from a little-wordian vector of <code class="language-plaintext highlighter-rouge">uint64_t</code>.  This could be optimized by doing conversion on demand.</p>

<p><code class="language-plaintext highlighter-rouge">OP_DIV</code>, <code class="language-plaintext highlighter-rouge">OP_MOD</code> and <code class="language-plaintext highlighter-rouge">OP_MUL</code> are implemented naively (comparison with libgmp’s big number operations shows more sophisticated approaches are astronomically faster).</p>

<h3 id="benchmarks-are-limits-low-enough-to-prevent-dos">Benchmarks: Are Limits Low Enough To Prevent DoS?</h3>

<h3 id="are-limits-high-enough-to-be-ignored">Are Limits High Enough to Be Ignored?</h3>

<p>We can remove the 520 byte limit</p>

<p>We still require a limit on total stack size: with a new segwit version this could be raised to 4000000, or left at 520,000 as per the current limit.</p>

<p>After 
I’ve had a series of posts looking at Script improvements.</p>

<p>In my previous post on <a href="/2023/10/20/examining-scriptpubkey-in-script.html#making-more-useful-templates-reducing-the-power-of-op_success">Examing scriptpubkeys in Script</a> I pointed out that there are cases where we want to require a certain script condition, but not an exact script: an example would be a vault-like covenant which requires a delay, but doesn’t care what else is in the script.</p>

<p>The problem with this is that in Taproot scripts, any unknown opcode (<code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>) will cause the entire script to succeed without being executed, so we need to hobble this slightly.  My previous proposal of some kind of separator was awkward, so I’ve developed a new idea which is simpler.</p>

<h1 id="introducing-op_segment">Introducing OP_SEGMENT</h1>

<p>Currently, the <a href="https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#specification">entire tapscript is scanned</a> for the <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> opcodes, and succeeds immediately if one it found.  This would be modified:</p>

<ol>
  <li>The tapscript is scanned for either <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> or <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>.</li>
  <li>If <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> is found, the script up to that point is executed.  If the script does not fail, scanning continues from that point.</li>
  <li>If <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code> is found, the script succeeds.</li>
</ol>

<p>This basically divides the script into <em>segments</em>, each executed serially.  It’s not quite as simple as “cut into pieces by OP_SEGMENT and examine one at a time” because the tapscript is allowed to contain things which would fail to decode altogether, after an <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>, and we want to retain that property.</p>

<p>When <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> is executed, it does nothing: it simply limits the range of <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> opcodes.</p>

<h1 id="implementation">Implementation</h1>

<p>The <code class="language-plaintext highlighter-rouge">ExecuteWitnessScript</code> would have to be refactored (probably as a separate <code class="language-plaintext highlighter-rouge">ExecuteTapScript</code> since 21 of its 38 lines are an “if Tapscript” anyway), and it also implies that the stack limits for the current tapscript would be enforced upon encountering <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code>, even if <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> were to follow after.</p>

<p>Interestingly, the core <code class="language-plaintext highlighter-rouge">EvalScript</code> function wouldn’t change except to ignore <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code>, as it’s already fairly flexible.</p>

<p>Note that I haven’t implemented it yet, so there may yet be surprises, but I plan to prototype after the idea has received some review!</p>

<p>Enjoy!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In my previous posts I’ve been carefully considering what bitcoin Script improvements we might want if we had introspection. Script was hobbled back in v0.3.1 due to denial-of-service issues: this has been a long-ongoing source of regret, but functions like OP_TXHASH bring Script limitations into clear focus.]]></summary></entry><entry><title type="html">Pay-to-Tapscript: Keyless Entry For Better Future Scripting</title><link href="https://rusty.ozlabs.org/2024/01/16/pay-to-tapscript.html" rel="alternate" type="text/html" title="Pay-to-Tapscript: Keyless Entry For Better Future Scripting" /><published>2024-01-16T00:00:00+10:30</published><updated>2024-01-16T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2024/01/16/pay-to-tapscript</id><content type="html" xml:base="https://rusty.ozlabs.org/2024/01/16/pay-to-tapscript.html"><![CDATA[<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#script-validation-rules">BIP-341</a> defines Taproot outputs as either a single key, or a key and some script info.  You use a dummy key if you only want to use the script.  This “tapscript” is currently very similar to Segwit v0 P2WSH, but carefully defined to be easily upgradable.</p>

<p>Unfortunately, when we actually try to use this upgrade flexibility (for <code class="language-plaintext highlighter-rouge">OP_CHECKTEMPLATEVERIFY</code> or <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> for example) we quickly find as Steven Roose <a href="https://github.com/bitcoin/bips/pull/1500#discussion_r1439511356">pointed out to me</a> that users also want a neutered Segwit v0 variant: using Tapscript requires a 33 byte penalty over simple P2WSH!</p>

<p>The fix is both simple and annoying: allowing the BIP-341 control block to be empty (or, perhaps, <code class="language-plaintext highlighter-rouge">32*m</code> bytes) to indicate the key is the NUMS point <code class="language-plaintext highlighter-rouge">lift_x(0x50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0)</code> as suggested by BIP-341.  BIP-341 suggests using a tweak of this key, which hides the existence of the script (if it were guessable) but forcing this at users expense was a mistake given the existence of P2WSH.</p>

<p>Regrettably, allowing this simple change requires (I think) using a Segwit version of 2, since BIP-341 defines v1 to fail if the control block is not an accepted length.  Others might have an idea if we want to roll in other changes at that point.</p>

<p>Enjoy!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[BIP-341 defines Taproot outputs as either a single key, or a key and some script info. You use a dummy key if you only want to use the script. This “tapscript” is currently very similar to Segwit v0 P2WSH, but carefully defined to be easily upgradable.]]></summary></entry><entry><title type="html">Transaction Stacking for Covenant Fee Minimization</title><link href="https://rusty.ozlabs.org/2024/01/08/txhash-tx-stacking.html" rel="alternate" type="text/html" title="Transaction Stacking for Covenant Fee Minimization" /><published>2024-01-08T00:00:00+10:30</published><updated>2024-01-08T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2024/01/08/txhash-tx-stacking</id><content type="html" xml:base="https://rusty.ozlabs.org/2024/01/08/txhash-tx-stacking.html"><![CDATA[<p>As I explore the use of Bitcoin Script for introspection, I am not <em>overly</em> concerned with total script size, because if common usage patterns emerge those can be soft-forked into new opcodes, or new address types.  But I have been concerned with constructions which require the use of Child-Pays-For-Parent for fee paying, as that makes the transaction significantly more expensive than using inline fees and Replace-By-Fee.</p>

<p>Lightning uses this kind of “anchor” construction, and although it’s only used in the forced-closure case, it’s wasteful of onchain space when it happens.  It also uses a “bring your own fee” construction for HTLC transactions, using <code class="language-plaintext highlighter-rouge">SIGHASH_SINGLE|SIGHASH_ANYONECANPAY</code> which means only the input and outputs are fixed, and the operation of this is much smoother in general.</p>

<p>(It’s not coincidence that my main contribution to the Eltoo construction was to use a similar single-input/output scheme to allow such last-minute fee binding and RBF).</p>

<p>More recently, Peter Todd argues that such <a href="https://petertodd.org/2023/v3-transactions-review#anchor-outputs-are-a-danger-to-mining-decentralization">inefficient fee bumping is a threat to decentralization</a> as it creates significant incentive to use out-of-band fees, which would have to be paid in advance and thus would favor large miners.</p>

<h1 id="stacking-transactions-adding-fees-later">Stacking Transactions: Adding Fees Later</h1>

<p>If you carefully construct your covenant to allow addition of a fee input (and usually a change output) later, you can avoid the expense of a child transaction and put the fees inline.</p>

<p>If you’re really clever, you can combine multiple covenant transactions into one transaction, and add a fee input/change output to all of them at once and reduce total costs even more.  I call this <em>stacking</em>, and my thesis is that Bitcoin fees will rise and eventually make such joining profitable, normal and necessary.</p>

<p>Note that such stacking requires real engineering work: we’ve seen how long it took Bitcoin exchanges to implement even simple batching!  And for full disclosure: stacking like this is already possible with Lightning with anchor outputs and HTLC transactions, which are signed with <code class="language-plaintext highlighter-rouge">SIGHASH_SINGLE|SIGHASH_ANYONECANPAY</code>, and yet I still haven’t implemented stacking in Core Lightning!</p>

<p>I now want to discuss the dangers of doing this incorrectly, and how <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> can support doing it in various scenarios.</p>

<h1 id="partial-validation-attacks-a-primer">Partial Validation Attacks: A Primer</h1>

<p>I vaguely recall first learning of this attack in the context of signing devices, but I cannot find a reference.  ~I’ll add one when some clever reader points it out!~ Greg Sanders’s post <a href="https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-August/014843.html">Hardware Wallet attacks by input ownership omission and fix</a> though I may have cribbed it from <a href="https://bitcoinops.org/en/newsletters/2020/05/13/#request-for-an-additional-taproot-signature-commitment">Bitcoin OpTech</a> (Greg he also <a href="https://x.com/theinstagibbs/status/1744175572832448554?s=20">mentioned jl2012 may have been involved</a>).</p>

<p>Consider a transaction designed to take a 1BTC input and pay Bob 0.1BTC, with the remaining 0.9BTC going to a change address.  Your software asks a signing device to sign the first input.  It checks the input, checks the outputs are correct, prompts the user (telling it we’re paying Bob 0.1BTC) and signs it.</p>

<p>Now consider a transaction which has two identical inputs.  Our naive signing device, asked to sign the first input, would see it as valid, and sign it.  If we then ask it to sign the second input it would also see it as valid, and sign it.  But the transaction would actually pay 1BTC to fees!</p>

<p>I call this a “Partial Validation Attack”, and the same problem can occur with stacking!  In this case, it’s the covenant checking the input, not the hardware wallet.  If it does not check other inputs (because it wants to allow you to add fees and/or stack other transactions together), and it would allow other covenants to validate the same outputs, it is vulnerable.</p>

<h2 id="partial-validation-exploit-a-covenant-example">Partial Validation Exploit: A Covenant Example.</h2>

<p>Imagine you want to create a covenant that forces a transaction to pay all its input amount to a given address, and you have <a href="https://github.com/bitcoin/bips/pull/1500/files/245c0b96463620a69a73a402f92987e8a52d8eed"><code class="language-plaintext highlighter-rouge">OP_TXHASH</code></a> and <code class="language-plaintext highlighter-rouge">OP_CAT</code>.</p>

<p>You want it to stack, so you simply validate that output #0 go to the given address, and that the amount match the input amount of the current input.  This is fairly easy, you can either use <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> to get the hashed amount from output #0, and again from the input and compare, or require the output supply the amount on the stack, duplicate it and hash it, then call <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> to hash the output #0 amount and the current input amount, and make sure that’s what they provided.</p>

<p>Then when you want to spend it, you can pay fees by adding as many inputs (and outputs) as you need without invalidating the transaction.</p>

<p>Now, you create two 1BTC outputs to this covenant address.  Mallory creates a transaction which spends both at once: it pays 1BTC to your required address (output #0) and sends the other 1BTC to their own address, stealing your funds.  Both inputs’ covenants check that output #0 pays the full amount to the required address, and are satisfied.  Oops!</p>

<h1 id="avoiding-partial-validation-issues-when-stacking-transactions">Avoiding Partial Validation Issues When Stacking Transactions</h1>

<p>This can avoided in one of four ways:</p>

<ol>
  <li>Specify the entire transaction, CTV-style.  But then you cannot add fees inline.</li>
  <li>Have each input examine all the other inputs.  This is limited since there is no looping in Script.</li>
  <li>Insist the current input also be at index #0, so there can only be one.</li>
  <li>Use relative output addressing, so we examine the output corresponding to the current input.</li>
</ol>

<p>Of these options, only the final one (relative output addressing) allows stacking, so obviously that’s my preferred approach.</p>

<p>Unfortunately, this stacking is only possible with current <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> if the number of inputs is equal to the number of outputs.  This can often be arranged, but any shared UTXO arrangement results in multi-in-single-out and single-in-multi-out.  Can we do better?</p>

<h1 id="stacking-odd-shaped-transactions">Stacking Odd-Shaped Transactions</h1>

<p>We can imagine <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> supporting an indexing scheme which lets you refer to “output = input-number * N” (I proposed this as a possibility in my BIP review).  (We can also imagine <code class="language-plaintext highlighter-rouge">OP_TX</code> which would let you push the current input number on the stack directly, to do this calculation yourself!).</p>

<p>This would let us stack have several 1-input/2-output txs.  But it wouldn’t let us stack different topologies, like a 1-in/2-out on top of a 2-in/1-out tx.</p>

<p>I considered adding an “output accumulator” where some OP_TXHASH field selector would increment the “next output” counter.  But writing it up I realized that this fails in the presence of <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> which can cause an input to be skipped; that would be a hard fork!</p>

<p>If we really want to do this in general, we would need to flag how many outputs each input “owns”, such as in the <code class="language-plaintext highlighter-rouge">nSequence</code> field.  And then have a “relative to owned outputs” modifier in <code class="language-plaintext highlighter-rouge">OP_TXHASH</code>.  As <code class="language-plaintext highlighter-rouge">nSequence</code> bits are limited and this would irreversibly consume some, I am reluctant to propose this unless real world usage of covenants (i.e. after they’re enabled by a soft-fork) shows it would have real onchain benefits.</p>

<h1 id="side-note-things-that-dont-work">Side Note: Things That Don’t Work</h1>

<p>You can imagine handing the output number(s) in the witness (and changing them when you stack the transactions), but that re-introduces the “partial transaction” bug.  Similarly, providing multiple signatures for different stacking cases would expose you to the issue.</p>

<h1 id="summary">Summary</h1>

<p>I believe stacking transactions is going to become popular to reduce fees: while this is currently easy for 1-input-1-output transactions, and the <code class="language-plaintext highlighter-rouge">OP_TXHASH</code> proposal makes it possible for N-input-N-outputs, I suspect the N-inputs-1-output an 1-input-N-output cases will be common (shared UTXOs), so we should try to allow those.  It would also be nice to design such that we can allow nSequence bits to indicate the number of associated outputs in a future soft fork.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[As I explore the use of Bitcoin Script for introspection, I am not overly concerned with total script size, because if common usage patterns emerge those can be soft-forked into new opcodes, or new address types. But I have been concerned with constructions which require the use of Child-Pays-For-Parent for fee paying, as that makes the transaction significantly more expensive than using inline fees and Replace-By-Fee.]]></summary></entry><entry><title type="html">OP_SEGMENT: Allowing Introspection to Check Partial Scripts</title><link href="https://rusty.ozlabs.org/2024/01/04/OP_SEGMENT.html" rel="alternate" type="text/html" title="OP_SEGMENT: Allowing Introspection to Check Partial Scripts" /><published>2024-01-04T00:00:00+10:30</published><updated>2024-01-04T00:00:00+10:30</updated><id>https://rusty.ozlabs.org/2024/01/04/OP_SEGMENT</id><content type="html" xml:base="https://rusty.ozlabs.org/2024/01/04/OP_SEGMENT.html"><![CDATA[<p>In my previous post on <a href="/2023/10/20/examining-scriptpubkey-in-script.html#making-more-useful-templates-reducing-the-power-of-op_success">Examing scriptpubkeys in Script</a> I pointed out that there are cases where we want to require a certain script condition, but not an exact script: an example would be a vault-like covenant which requires a delay, but doesn’t care what else is in the script.</p>

<p>The problem with this is that in Taproot scripts, any unknown opcode (<code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>) will cause the entire script to succeed without being executed, so we need to hobble this slightly.  My previous proposal of some kind of separator was awkward, so I’ve developed a new idea which is simpler.</p>

<h1 id="introducing-op_segment">Introducing OP_SEGMENT</h1>

<p>Currently, the <a href="https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki#specification">entire tapscript is scanned</a> for the <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> opcodes, and succeeds immediately if one it found.  This would be modified:</p>

<ol>
  <li>The tapscript is scanned for either <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> or <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>.</li>
  <li>If <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> is found, the script up to that point is executed.  If the script does not fail, scanning continues from that point.</li>
  <li>If <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code> is found, the script succeeds.</li>
</ol>

<p>This basically divides the script into <em>segments</em>, each executed serially.  It’s not quite as simple as “cut into pieces by OP_SEGMENT and examine one at a time” because the tapscript is allowed to contain things which would fail to decode altogether, after an <code class="language-plaintext highlighter-rouge">OP_SUCCESSx</code>, and we want to retain that property.</p>

<p>When <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code> is executed, it does nothing: it simply limits the range of <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> opcodes.</p>

<h1 id="implementation">Implementation</h1>

<p>The <code class="language-plaintext highlighter-rouge">ExecuteWitnessScript</code> would have to be refactored (probably as a separate <code class="language-plaintext highlighter-rouge">ExecuteTapScript</code> since 21 of its 38 lines are an “if Tapscript” anyway), and it also implies that the stack limits for the current tapscript would be enforced upon encountering <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code>, even if <code class="language-plaintext highlighter-rouge">OP_SUCCESS</code> were to follow after.</p>

<p>Interestingly, the core <code class="language-plaintext highlighter-rouge">EvalScript</code> function wouldn’t change except to ignore <code class="language-plaintext highlighter-rouge">OP_SEGMENT</code>, as it’s already fairly flexible.</p>

<p>Note that I haven’t implemented it yet, so there may yet be surprises, but I plan to prototype after the idea has received some review!</p>

<p>Enjoy!</p>]]></content><author><name></name></author><summary type="html"><![CDATA[In my previous post on Examing scriptpubkeys in Script I pointed out that there are cases where we want to require a certain script condition, but not an exact script: an example would be a vault-like covenant which requires a delay, but doesn’t care what else is in the script.]]></summary></entry></feed>