Unnamed repository; edit this file 'description' to name the repository.
 help / color / mirror / Atom feed
From: jeremy <jeremy.l.rubin@gmail.com>
To: Bitcoin Development Mailing List <bitcoindev@googlegroups.com>
Subject: Re: [bitcoindev] [BIP-0054] 64-Byte Transactions and Potential Legitimate Uses
Date: Sat, 2 May 2026 11:09:12 -0700 (PDT)	[thread overview]
Message-ID: <8a82e9d6-b013-4b7f-ba9f-f91f50b1bbe3n@googlegroups.com> (raw)
In-Reply-To: <CAGL6+mGeqx8TvWSfSg_FdrbHVy+3Sk5VmQ+_WPjGjhXbp=LiBA@mail.gmail.com>


[-- Attachment #1.1: Type: text/plain, Size: 14610 bytes --]

Chris,

These examples should work and could be adapted to a different funding 
input (I stubbed out the actual signature with just a preimage reveal for 
simplicity).


funding witnessScript: 
a820ff76f4f923cd475f27097b3822640a9b441894f3d4f742fe70e745e986da388e87 
funding scriptPubKey: 
002080c153db1ea3461a2b70126f853189fc34d7d9926f083dfb2bffae8ae2bb1130 
funding txid: 
166ef40e58f1a62e01d610023ca7935eef372cf3c1239d81430dffbd1fe8fe9e 
funding tx hex: 
02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff020101ffffffff01a08601000000000022002080c153db1ea3461a2b70126f853189fc34d7d9926f083dfb2bffae8ae2bb113000000000 

182 | 
spending output scriptPubKey: 020002b2 
spending stripped size: 64 
spending full size: 114 
spending txid: 
44c752f499326a59a78259b455875d296108184b7f25b9a0b41bc018af7aedd8 
spending wtxid: 
1e6eb4de698f079cbaa0fdf47d7ab5e1bb4939fae42652f295dd05d15930074d 
spending tx stripped hex: 
02000000019efee81fbdff0d43819d23c1f32c37ef5e93a73c0210d6012ea6f1580ef46e160000000000ffffffff01905f01000000000004020002b200000000 

spending tx full hex: 
020000000001019efee81fbdff0d43819d23c1f32c37ef5e93a73c0210d6012ea6f1580ef46e160000000000ffffffff01905f01000000000004020002b2020a62697035342064656d6f23a820ff76f4f923cd475f27097b3822640a9b441894f3d4f742fe70e745e986da388e8700000000 



*$ decodrawtransaction $(cat funding_tx.hex)*

{
  "txid": 
"166ef40e58f1a62e01d610023ca7935eef372cf3c1239d81430dffbd1fe8fe9e",
  "hash": 
"166ef40e58f1a62e01d610023ca7935eef372cf3c1239d81430dffbd1fe8fe9e",
  "version": 2,
  "size": 96,
  "vsize": 96,
  "weight": 384,
  "locktime": 0,
  "vin": [
    {
      "coinbase": "0101",
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00100000,
      "n": 0,
      "scriptPubKey": {
        "asm": "0 
80c153db1ea3461a2b70126f853189fc34d7d9926f083dfb2bffae8ae2bb1130",
        "desc": 
"addr(bcrt1qsrq48kc75drp52mszfhc2vvfls6d0kvjduyrm7etl7hg4c4mzycqmw604m)#2rrnsnlc",
        "hex": 
"002080c153db1ea3461a2b70126f853189fc34d7d9926f083dfb2bffae8ae2bb1130",
        "address": 
"bcrt1qsrq48kc75drp52mszfhc2vvfls6d0kvjduyrm7etl7hg4c4mzycqmw604m",
        "type": "witness_v0_scripthash"
      }
    }
  ]
}



*$ decodrawtransaction $(cat spending_tx_stripped.hex)*


{
  "txid": 
"44c752f499326a59a78259b455875d296108184b7f25b9a0b41bc018af7aedd8",
  "hash": 
"44c752f499326a59a78259b455875d296108184b7f25b9a0b41bc018af7aedd8",
  "version": 2,
  "size": 64,
  "vsize": 64,
  "weight": 256,
  "locktime": 0,
  "vin": [
    {
      "txid": 
"166ef40e58f1a62e01d610023ca7935eef372cf3c1239d81430dffbd1fe8fe9e",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00090000,
      "n": 0,
      "scriptPubKey": {
        "asm": "512 OP_CHECKSEQUENCEVERIFY",
        "desc": "raw(020002b2)#vceh5z0j",
        "hex": "020002b2",
        "type": "nonstandard"
      }
    }
  ]
}
*$ decodrawtransaction $(cat spending_tx_full.hex)*

{
  "txid": 
"44c752f499326a59a78259b455875d296108184b7f25b9a0b41bc018af7aedd8",
  "hash": 
"1e6eb4de698f079cbaa0fdf47d7ab5e1bb4939fae42652f295dd05d15930074d",
  "version": 2,
  "size": 114,
  "vsize": 77,
  "weight": 306,
  "locktime": 0,
  "vin": [
    {
      "txid": 
"166ef40e58f1a62e01d610023ca7935eef372cf3c1239d81430dffbd1fe8fe9e",
      "vout": 0,
      "scriptSig": {
        "asm": "",
        "hex": ""
      },
      "txinwitness": [
        "62697035342064656d6f",
        
"a820ff76f4f923cd475f27097b3822640a9b441894f3d4f742fe70e745e986da388e87"
      ],
      "sequence": 4294967295
    }
  ],
  "vout": [
    {
      "value": 0.00090000,
      "n": 0,
      "scriptPubKey": {
        "asm": "512 OP_CHECKSEQUENCEVERIFY",
        "desc": "raw(020002b2)#vceh5z0j",
        "hex": "020002b2",
        "type": "nonstandard"
      }
    }
  ]
}


```python3
#!/usr/bin/env python3
from __future__ import annotations

import hashlib
import struct


OP_SHA256 = 0xA8
OP_EQUAL = 0x87
OP_CHECKSEQUENCEVERIFY = 0xB2


def sha256(b: bytes) -> bytes:
    return hashlib.sha256(b).digest()


def hash256(b: bytes) -> bytes:
    return hashlib.sha256(hashlib.sha256(b).digest()).digest()


def hash256_be_hex(b: bytes) -> str:
    return hash256(b)[::-1].hex()


def compact_size(n: int) -> bytes:
    if n < 0:
        raise ValueError("negative compact size")
    if n < 253:
        return bytes([n])
    if n <= 0xFFFF:
        return b"\xfd" + struct.pack("<H", n)
    if n <= 0xFFFFFFFF:
        return b"\xfe" + struct.pack("<I", n)
    return b"\xff" + struct.pack("<Q", n)


def i32(n: int) -> bytes:
    return struct.pack("<i", n)


def u32(n: int) -> bytes:
    return struct.pack("<I", n)


def u64(n: int) -> bytes:
    return struct.pack("<Q", n)


def ser_output(value_sat: int, script_pubkey: bytes) -> bytes:
    return u64(value_sat) + compact_size(len(script_pubkey)) + script_pubkey


def ser_input(prev_txid_be_hex: str, vout: int, script_sig: bytes = b"", 
sequence: int = 0xFFFFFFFF) -> bytes:
    # Transaction outpoints serialize txids little-endian.
    prev_txid_le = bytes.fromhex(prev_txid_be_hex)[::-1]
    return (
        prev_txid_le
        + u32(vout)
        + compact_size(len(script_sig))
        + script_sig
        + u32(sequence)
    )


# --------------------------------------------------------------------
# Funding transaction
# --------------------------------------------------------------------
#
# This is a coinbase-like funding transaction for a decode/test vector.
# It pays 100_000 sats to a native P2WSH output.
#
# witnessScript:
#
#   OP_SHA256 <sha256("bip54 demo")> OP_EQUAL
#
# The spending transaction must reveal the preimage "bip54 demo" in witness.

secret = b"bip54 demo"
secret_hash = sha256(secret)

witness_script = (
    bytes([OP_SHA256])
    + bytes([0x20])       # push 32 bytes
    + secret_hash
    + bytes([OP_EQUAL])
)

# Native v0 P2WSH scriptPubKey:
#
#   OP_0 <sha256(witness_script)>
#
funding_script_pubkey = b"\x00\x20" + sha256(witness_script)

coinbase_script_sig = b"\x01\x01"  # BIP34-style height=1 push; enough for 
a decode vector.

funding_tx = (
    i32(2)
    + compact_size(1)
    + bytes(32)                  # coinbase prevout txid = 0x00..00
    + u32(0xFFFFFFFF)            # coinbase prevout index
    + compact_size(len(coinbase_script_sig))
    + coinbase_script_sig
    + u32(0xFFFFFFFF)
    + compact_size(1)
    + ser_output(100_000, funding_script_pubkey)
    + u32(0)
)

funding_txid = hash256_be_hex(funding_tx)


# --------------------------------------------------------------------
# Spending transaction
# --------------------------------------------------------------------
#
# This spends funding_tx output 0.
#
# The sole output scriptPubKey is:
#
#   <512> OP_CHECKSEQUENCEVERIFY
#
# Minimal ScriptNum encoding for decimal 512 is little-endian 00 02,
# so the full script is:
#
#   02 00 02 b2
#
# This spending transaction has:
#
#   witness-stripped size = exactly 64 bytes
#   full witness serialization = larger than 64 bytes

csv512_script_pubkey = bytes([0x02, 0x00, 0x02, OP_CHECKSEQUENCEVERIFY])

spending_input = ser_input(
    prev_txid_be_hex=funding_txid,
    vout=0,
    script_sig=b"",
    sequence=0xFFFFFFFF,
)

spending_output = ser_output(90_000, csv512_script_pubkey)

spending_tx_stripped = (
    i32(2)
    + compact_size(1)
    + spending_input
    + compact_size(1)
    + spending_output
    + u32(0)
)

# P2WSH witness stack:
#
#   <secret> <witness_script>
#
spending_witness = (
    compact_size(2)
    + compact_size(len(secret))
    + secret
    + compact_size(len(witness_script))
    + witness_script
)

spending_tx_full = (
    i32(2)
    + b"\x00\x01"               # SegWit marker + flag
    + compact_size(1)
    + spending_input
    + compact_size(1)
    + spending_output
    + spending_witness
    + u32(0)
)

assert len(spending_tx_stripped) == 64
assert csv512_script_pubkey.hex() == "020002b2"

print("funding witnessScript:", witness_script.hex())
print("funding scriptPubKey: ", funding_script_pubkey.hex())
print("funding txid:         ", funding_txid)
print("funding tx hex:       ", funding_tx.hex())
print()
print("spending output scriptPubKey:", csv512_script_pubkey.hex())
print("spending stripped size:      ", len(spending_tx_stripped))
print("spending full size:          ", len(spending_tx_full))
print("spending txid:               ", hash256_be_hex(spending_tx_stripped))
print("spending wtxid:              ", hash256_be_hex(spending_tx_full))
print("spending tx stripped hex:    ", spending_tx_stripped.hex())
print("spending tx full hex:        ", spending_tx_full.hex())
```

On Saturday, May 2, 2026 at 8:56:25 AM UTC-7 Chris Stewart wrote:

> I do concede your point that witness stripping isn't well understood by 
> the general community. Here is a writeup I did for readers of the mailing 
> list that may not understand what Jeremy is talking about the 
> characteristics of a 64 byte transaction[0]. There is a big distinction 
> between pre-segwit 64 byte transactions and segwit 64 byte transactions.
>
>
> > A transaction that donates to a future miner from a segwit (any version) 
> output via a spend to something like <512> OP_CSV (-> push2 bytes 512 csv 
> -> 0x02 0x00 0x02 0xb2)
>
> I find this confusing. Can you give 2 hex encoded transactions (funding 
> transaction, spending transaction) that do this? regtest is fine, i can 
> just decode locally.
>
> Splitting hairs semantically if understand the transactions you are 
> proposing above, "donating" to a "future miner" i.e. sending money to *anyone 
> (not a specific someone)* in the future that can mine a block - doesn't 
> seem much different to me than "anyone can spend the funds". I'll await 
> your transactions though in the case that I am missing something.
>
> [0] - 
> https://delvingbitcoin.org/t/great-consensus-cleanup-revival/710/73?u=chris_stewart_5
>
> On Fri, May 1, 2026 at 4:15 PM jeremy <jeremy....@gmail.com> wrote:
>
>> For fun, let's start with a pop-quiz:
>>
>> Select all that apply: There can exist a transaction of ___ bytes 
>> serialized size that BIP-0054's 64-byte restriction invalidates:
>>
>> A) 64 Bytes
>> B) 0 Bytes
>> C) 1.5MB
>> D) 32 Bytes
>> E) 5MB
>>
>>
>> The answer is A, 64 Bytes, and -- perhaps surprisingly -- C, 1.5MB. 
>>
>> Why is this the case?
>>
>> BIP-0054 uses the term 64-byte transaction, but defines it as follows:
>>
>> *> Transactions whose witness-stripped serialized size is exactly 64 
>> bytes are invalid.*
>>
>> In a [personally run] straw-poll of devs at a recent conference, no-one 
>> knew this precise edge condition or that the transactions could have a 
>> meaningful witness. For clarity, the restriction on bytes is on 
>> INVALID_TX_NONWITNESS_SIZE, not on the size with Witness.
>>
>> Therefore, it is more accurate to refer to this in all sentences 
>> throughout the BIP as:
>>
>> *> transactions with exactly 64 bytes of non-witness data*,
>>
>> due to the propensity for confusion.
>>
>> BIP-0054 also makes a comment that the transactions it invalidates are 
>> essentially useless:
>>
>>
>> *> 64-byte transactions can only contain a scriptPubKey that lets anyone 
>> spend the funds, or one that burns them.*
>>
>> This is not strictly correct. Here are a few examples of current and 
>> future uses for 64-byte transactions:
>>
>> *Current Uses:*
>> - A transaction that donates to a future miner from a segwit (any 
>> version) output via a spend to something like <512> OP_CSV (-> push2 bytes 
>> 512 csv -> 0x02 0x00 0x02 0xb2)
>> - That same output which is used as a connector output for things that 
>> should be claimed by a miner at a future time
>> - Pay-to-Anchor / ephemeral anchor outputs -- while typically p2a is for 
>> txns you want to add a subsidy ability, a 64-byte txn could be used to shim 
>> a keyed anchor to a p2a output after a certain delay.
>>
>> *Future Uses:*
>> - Future work which might use output scripts for e.g. Transaction Sponsor 
>> encodings
>> - Future covenants work which encodes time-of-creation run scripts that 
>> e.g. quine an input; possibly in conjunction with sponsors
>> - Future where we have expensive reusable PQ or Contract public keys that 
>> are posted once and referred to by index
>>
>>
>> While, in a sense, current uses are much more concerning than future 
>> uses, with introspection opcodes, it might create substantive additional 
>> complexity to ensure that there is always a valid way to add a padding byte 
>> without upsetting a state machine.
>>
>> As there are now documented use cases for 64-byte transactions that this 
>> proposal makes more difficult to do, I recommend replacing the text in the 
>> BIP that says
>>
>>
>> *> 64-byte transactions can only contain a scriptPubKey that lets anyone 
>> spend the funds, or one that burns them. *
>> With something like:
>>
>> *> There are documented use cases for 64-byte transactions that this 
>> proposal makes more difficult to cleanly do, but we do not believe these 
>> use cases will ever be valuable or worth protecting.*
>>
>> Or a more accurate reflection of the BIP-0054 authors' opinion.
>>
>> Jeremy
>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "Bitcoin Development Mailing List" group.
>>
> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to bitcoindev+...@googlegroups.com.
>> To view this discussion visit 
>> https://groups.google.com/d/msgid/bitcoindev/123e5545-2eda-4eca-9532-4f4cea2b83ecn%40googlegroups.com 
>> <https://groups.google.com/d/msgid/bitcoindev/123e5545-2eda-4eca-9532-4f4cea2b83ecn%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>>
>

-- 
You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/8a82e9d6-b013-4b7f-ba9f-f91f50b1bbe3n%40googlegroups.com.

[-- Attachment #1.2: Type: text/html, Size: 19552 bytes --]

  reply	other threads:[~2026-05-02 18:09 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-01 21:14 [bitcoindev] [BIP-0054] 64-Byte Transactions and Potential Legitimate Uses jeremy
2026-05-01 22:03 ` eric
2026-05-14 13:50   ` 'Antoine Poinsot' via Bitcoin Development Mailing List
2026-05-02  5:29 ` Anthony Towns
2026-05-02 15:26 ` Chris Stewart
2026-05-02 18:09   ` jeremy [this message]
2026-05-06 11:10 ` 'Antoine Poinsot' via Bitcoin Development Mailing List
2026-05-06 21:35   ` jeremy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8a82e9d6-b013-4b7f-ba9f-f91f50b1bbe3n@googlegroups.com \
    --to=jeremy.l.rubin@gmail.com \
    --cc=bitcoindev@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox