From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Mon, 15 Jun 2026 11:11:24 -0700 Received: from mail-qt1-f188.google.com ([209.85.160.188]) by mail.fairlystable.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1wZBmI-00073t-KW for bitcoindev@gnusha.org; Mon, 15 Jun 2026 11:11:24 -0700 Received: by mail-qt1-f188.google.com with SMTP id d75a77b69052e-5175aa1a54bsf62760741cf.1 for ; Mon, 15 Jun 2026 11:11:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlegroups.com; s=20251104; t=1781547068; x=1782151868; darn=gnusha.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:x-original-sender:mime-version :subject:references:in-reply-to:message-id:to:from:date:sender:from :to:cc:subject:date:message-id:reply-to; bh=91gPzJtIKOb7n9SkTx2+3xqH1osDMIPb6n+HhHfUuBQ=; b=xpMwWuPqLEUfgHS629EzA85xlshmxGLkOkk6wGb478H7necLBAUxkmiLsIiPcdqfve YXUvN2HDcSi8jI2RKs1trOQ27FaQmIPGs7AgKo0bmX2/x4Mb3ldyEt4GmoNwleu3Bxdu KuxWgOsvXhs8J8woh/IFgDchHptFJZVbxwGj/Di3sYrdvIRJZPjwKGWLMmP8HFy0F374 7wDVLToUX3F9rHLqKzTeR/6vn+ChUHu5XQksW3WxLrS7Px12cI9B/tb1MS+bvXPoCiIn Y3ekVIIWJ2hBRPhe6NeQtR0UqkhD69QebsJa8K4AkvCWqfARYVqUvqoi/+Ugt25rrOAB 79FA== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781547068; x=1782151868; darn=gnusha.org; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:x-original-sender:mime-version :subject:references:in-reply-to:message-id:to:from:date:from:to:cc :subject:date:message-id:reply-to; bh=91gPzJtIKOb7n9SkTx2+3xqH1osDMIPb6n+HhHfUuBQ=; b=aBQF1FmgJaLyfvZIAwKx5XDTFg3v6Ta6vU4iaDfbKOlDxwTmNwcMAfenC1uslh65iB o9LdbWEnv4HUysQY/fE/Ctr4ZwX34O5M21my5+iy6NUqhzyJee9LNGRsnLTXAQY10hLm CSLoq427XbRJ+VVu0J1x4KxKJf1d1cK3mGwEPKFQiyJqML1U+nRHZP8LELL26ab3y3xp jIihMnkeexoqOwYiJJ1OzbQQNRewT+9xPjECz+/1d/TxOhJnwxoRBsxrgRdGTg6t687a sxAqbrICiuJYMIr3Cdy38F3NLcIY46fS1kHajs0b/KbjyefpNOn8FZYZeOyNJj/KT+iF MnLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781547068; x=1782151868; h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post :list-id:mailing-list:precedence:x-original-sender:mime-version :subject:references:in-reply-to:message-id:to:from:date:x-beenthere :x-gm-message-state:sender:from:to:cc:subject:date:message-id :reply-to; bh=91gPzJtIKOb7n9SkTx2+3xqH1osDMIPb6n+HhHfUuBQ=; b=Ud5rcOumUje7/8gXFKX6LtOddzKu0lSNjdZH9ekzmiJpuQy0yRIH6s72s09aEim20l Dya0gcwtcd9pHAmJkHpZUi3XlEMGZBiKKlqQVuR9za0pFCBSPTCPelNp7/Ot19XJoCNo LyRuDDCgEB4ykeFeYSOI/J6+Gn3rYBDMu7JzjaNMtKHCpQK6dboZ3KKHpLhjJsye8q/O 06PBXLzs6jNjvAjoZAlErrmlqbzEVSBsL+wBJc8cyZCaAnZOh7OufHsH6hnmjp7KvLtI bP51M6klKsr91l5/xevGUBgr9vKwSn3STD1nkO2k6HNf4dVEjl4w+jQi5ByHD6OImgxq slpA== Sender: bitcoindev@googlegroups.com X-Forwarded-Encrypted: i=1; AFNElJ858WqtNsJ8KOZmQKk1+QQr/2hP3CllM4j8/esv/zkGAhFhYftXH8nA835vROMwGEdijD1qbAzl2yy/@gnusha.org X-Gm-Message-State: AOJu0YyhuggJXYDEGe/2UoEL/AJBZmEFvJOF3gu9lgwQ1dv2ZrSni8iL 63rAYW0b/wCG5VH1CN2AlBRjB6Z7/rMDsMQ0YaZGpDHvayaGHQQnlJTT X-Received: by 2002:a05:622a:468c:b0:517:b68a:8d8f with SMTP id d75a77b69052e-519915988d4mr1510621cf.19.1781547068056; Mon, 15 Jun 2026 11:11:08 -0700 (PDT) X-BeenThere: bitcoindev@googlegroups.com; h="AX0PUUeQEhLVumZVz+GoXrj4c2eW6yF8PSWURsSEv+RyT31myw==" Received: by 2002:ac8:7f91:0:b0:517:606e:82af with SMTP id d75a77b69052e-517f9e16531ls89182631cf.1.-pod-prod-05-us; Mon, 15 Jun 2026 11:11:00 -0700 (PDT) X-Received: by 2002:a05:620a:bc8:b0:915:a73e:3544 with SMTP id af79cd13be357-917f1c56720mr2109625485a.56.1781547060549; Mon, 15 Jun 2026 11:11:00 -0700 (PDT) Received: by 2002:a05:690c:64:b0:7d0:63a3:69e7 with SMTP id 00721157ae682-7f795ee1f65ms7b3; Mon, 15 Jun 2026 10:20:53 -0700 (PDT) X-Received: by 2002:a05:690c:e350:b0:7dd:1616:4563 with SMTP id 00721157ae682-7f8c1e18933mr120377357b3.22.1781544052138; Mon, 15 Jun 2026 10:20:52 -0700 (PDT) Date: Mon, 15 Jun 2026 10:20:51 -0700 (PDT) From: jeremy To: Bitcoin Development Mailing List Message-Id: In-Reply-To: <00be6fe9-7178-4069-9722-5595fde55b72@mattcorallo.com> References: <00be6fe9-7178-4069-9722-5595fde55b72@mattcorallo.com> Subject: Re: [bitcoindev] Prohibit Merkle Internal Node Preimages That Encode Minimal 64-Byte Transactions MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_106176_1697668896.1781544051665" X-Original-Sender: Jeremy.L.Rubin@gmail.com Precedence: list Mailing-list: list bitcoindev@googlegroups.com; contact bitcoindev+owners@googlegroups.com List-ID: X-Google-Group-Id: 786775582512 List-Post: , List-Help: , List-Archive: , List-Unsubscribe: , X-Spam-Score: -0.5 (/) ------=_Part_106176_1697668896.1781544051665 Content-Type: multipart/alternative; boundary="----=_Part_106177_1531744813.1781544051665" ------=_Part_106177_1531744813.1781544051665 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable SDL sent me a note clarifying I missed a rule to restrict the prevout index= =20 to in-range. The updated rule should be: The block is invalid if P satisfies all of the following: 1.=20 =20 P[4] =3D=3D 0x01. 2.=20 =20 P[41] is one of 0, 1, 2, 3, 4. 3.=20 =20 Let x =3D P[41]. 4.=20 =20 Let sequence_pos =3D 42 + x. 5.=20 =20 Let vout_count_pos =3D sequence_pos + 4. 6.=20 =20 Let value_pos =3D vout_count_pos + 1. 7.=20 =20 Let scriptpubkey_len_pos =3D value_pos + 8. 8.=20 =20 P[vout_count_pos] =3D=3D 0x01. 9.=20 =20 P[scriptpubkey_len_pos] =3D=3D 4 - x. 10.=20 =20 Let locktime_pos =3D scriptpubkey_len_pos + 1 + (4 - x). 11.=20 =20 locktime_pos + 4 =3D=3D 64. 12.=20 =20 The 8-byte little-endian integer at P[value_pos..value_pos+7] is in=20 MoneyRange. 13.=20 =20 * The 4-byte little-ending integer at P[4+1+32] is less than or equal to= =20 VOutRange* =20 *VOutRange is *111,111 ( from 1e6 / (1 byte length + 8 bytes amount)). -------- This contributes another 15.2 bits, giving approximately 51 bits of=20 grinding for an invalid right hand side. This is still feasible adversarially, but unlikely to ever happen by=20 accident (or in deeper internal nodes). In addition, we can also add a policy rule to no accept or realy txns which= =20 happen to hash to this 51 bit target shape -- this doesn't need to be=20 consensus enforced, but provides a bit of protection to legacy nodes. -------------- This doesn't seem to effect the adversarial analysis much, just makes it=20 more expensive. If you can get a valid RHS and LHS sent to a legacy node=20 and predict their Txn Ordering you could cause a violation. On Tuesday, June 9, 2026 at 2:39:53=E2=80=AFPM UTC-4 Matt Corallo wrote: Hey Jeremy,=20 While this is certainly an interesting alternative mitigation against some= =20 attacks, the fact that it=20 implies that miners have to change their block-building software to handle= =20 potential malicious=20 transaction selection which can cause them to create invalid blocks seems= =20 to make this a total=20 nonstarter.=20 Not breaking existing deployed software, including miners who in some cases= =20 have custom=20 block-building logic is obviously an important goal for soft forks. Given= =20 there's no (known) use of=20 64-byte transactions anywhere (and they've been non-standard for a longggg= =20 time!) its hard to argue=20 banning 64-byte transactions would have any similar issues. As others have= =20 noted, banning 64-byte=20 transactions seems to be a more complete fix as well.=20 Matt=20 On 6/1/26 1:46 PM, jeremy wrote:=20 > Esteemed Colleagues,=20 >=20 > As a result of some of my research on 64-byte transactions, I'd like to= =20 discuss an alternative soft=20 > fork proposal that preserves the ability to encode 64-byte transactions= =20 while offering protection to=20 > SPV users (who must make a small patch to validate the path property).=20 >=20 > The rule, stated simply, is:=20 >=20 > A block is invalid if any Merkle Tree 64-byte preimage has the exact byte= =20 structure of a minimal=20 > one-input, one-output, witness stripped transaction.=20 >=20 > [With the miracle of GPT,] I've drafted a relatively complete BIP for=20 discussion.=20 >=20 > Happy International Children's Day,=20 >=20 > Jeremy=20 >=20 > p.s. I will later propose potentially a couple other mitigations=20 separately, for discussion as well.=20 >=20 >=20 ---------------------------------------------------------------------------= -------------------------=20 >=20 > BIP: TBD=20 > Layer: Consensus (soft fork)=20 > Title: Prohibit Merkle Internal Node Preimages That Encode Minimal=20 64-Byte Transactions=20 > Author: TBD=20 > Status: Draft=20 > Type: Standards Track=20 > Created: 2026-06-01=20 > License: BSD-2-Clause=20 >=20 > *Abstract*=20 >=20 > This document specifies a consensus rule that invalidates a block if any= =20 transaction Merkle tree=20 > internal node preimage encodes a minimal 64-byte transaction.=20 >=20 > For each internal Merkle node, Bitcoin computes:=20 >=20 > parent =3D SHA256d(left || right)=20 >=20 > *=20 > *=20 >=20 > where leftand rightare 32-byte hashes. The 64-byte string left || rightis= =20 the internal node preimage.=20 >=20 > After activation, a block is invalid if any such 64-byte preimage has the= =20 exact byte structure of a=20 > minimal one-input, one-output, non-witness transaction.=20 >=20 > This prevents a 64-byte transaction serialization from being malleated=20 into an internal Merkle node=20 > preimage in SPV transaction inclusion proofs. It does not make 64-byte=20 transactions invalid in general.=20 >=20 > *Motivation*=20 >=20 > Bitcoin transaction identifiers and transaction Merkle internal nodes are= =20 both computed with double=20 > SHA256:=20 >=20 > txid =3D SHA256d(serialized_transaction)=20 >=20 > parent =3D SHA256d(left_child_hash || right_child_hash)=20 >=20 > *=20 > *=20 >=20 > If a valid transaction serialization is exactly 64 bytes, the same byte= =20 string can also be=20 > interpreted as the concatenation of two 32-byte Merkle child hashes:=20 >=20 > serialized_transaction =3D left_child_hash || right_child_hash=20 >=20 > *=20 > *=20 >=20 > This creates an ambiguity between a transaction leaf preimage and an=20 internal node preimage.=20 >=20 > An SPV verifier that accepts a Merkle proof without authenticating the=20 full tree shape can be made=20 > to accept a proof terminating at an internal node rather than at an=20 actual transaction leaf.=20 >=20 > This proposal removes that ambiguity by forbidding Merkle internal node= =20 preimages that have the only=20 > practical 64-byte transaction encoding shape.=20 >=20 > *SegWit and transaction identifiers*=20 >=20 > Since SegWit activation, Bitcoin transactions have two related=20 identifiers:=20 >=20 > txid =3D SHA256d(legacy serialization)=20 >=20 > wtxid =3D SHA256d(witness serialization)=20 >=20 > *=20 > *=20 >=20 > The distinction is important for understanding this proposal.=20 >=20 > A SegWit transaction is serialized on the wire as:=20 >=20 > nVersion=20 >=20 > marker=20 >=20 > flag=20 >=20 > vin=20 >=20 > vout=20 >=20 > witness=20 >=20 > nLockTime=20 >=20 > *=20 > *=20 >=20 > where:=20 >=20 > marker =3D 0x00=20 >=20 > flag =3D 0x01=20 >=20 > *=20 > *=20 >=20 > The marker and flag bytes indicate that witness data is present.=20 >=20 > However, the transaction identifier (txid) is not computed from this=20 witness serialization. Instead,=20 > the txidis computed from the legacy serialization:=20 >=20 > nVersion=20 >=20 > vin=20 >=20 > vout=20 >=20 > nLockTime=20 >=20 > *=20 > *=20 >=20 > with the marker, flag, and witness fields omitted.=20 >=20 > Therefore:=20 >=20 > txid =3D SHA256d(non-witness serialization)=20 >=20 > *=20 > *=20 >=20 > while:=20 >=20 > wtxid =3D SHA256d(full witness serialization)=20 >=20 > *=20 > *=20 >=20 > The transaction Merkle root committed in the block header is built from= =20 transaction identifiers=20 > (txids), not witness transaction identifiers (wtxids).=20 >=20 > Consequently:=20 >=20 > Merkle root =3D Merkle(txid_0, txid_1, ..., txid_n)=20 >=20 > *=20 > *=20 >=20 > and not:=20 >=20 > Merkle(wtxid_0, wtxid_1, ..., wtxid_n)=20 >=20 > *=20 > *=20 >=20 > This means that the marker byte (0x00), flag byte (0x01), and witness=20 data never appear in the=20 > transaction Merkle tree committed by the block header.=20 >=20 > SegWit does define a separate witness Merkle tree whose root is committed= =20 through the coinbase=20 > witness commitment, but that witness Merkle tree is distinct from the=20 transaction Merkle tree=20 > discussed in this proposal.=20 >=20 > As a result, the ambiguity addressed by this proposal concerns only=20 transaction identifiers (txids)=20 > and the transaction Merkle root. The SegWit marker and flag bytes are=20 irrelevant to the transaction=20 > Merkle root because they are excluded from txidserialization.=20 >=20 > *Minimal 64-byte transaction shape*=20 >=20 > This proposal is concerned with the serialization used to compute a=20 transaction's txid.=20 >=20 > For legacy transactions, and for SegWit transactions when computing the= =20 txid, the serialization=20 > format is:=20 >=20 > 4 bytes nVersion=20 >=20 > 1 byte vin count =3D 0x01=20 >=20 > 36 bytes prevout=20 >=20 > 1 byte scriptSig length =3D x=20 >=20 > x bytes scriptSig=20 >=20 > 4 bytes nSequence=20 >=20 > 1 byte vout count =3D 0x01=20 >=20 > 8 bytes nValue=20 >=20 > 1 byte scriptPubKey length =3D y=20 >=20 > y bytes scriptPubKey=20 >=20 > 4 bytes nLockTime=20 >=20 > *=20 > *=20 >=20 > Notably, this serialization does not include:=20 >=20 > marker=20 >=20 > flag=20 >=20 > witness stack=20 >=20 > *=20 > *=20 >=20 > because those fields are excluded from txidcomputation.=20 >=20 > The fixed overhead is:=20 >=20 > 4 + 1 + 36 + 1 + 4 + 1 + 8 + 1 + 4 =3D 60 bytes=20 >=20 > *=20 > *=20 >=20 > Therefore, for total serialized size 64:=20 >=20 > x + y =3D 4=20 >=20 > *=20 > *=20 >=20 > There are exactly five possible script-length splits:=20 >=20 > scriptSig length scriptPubKey length=20 >=20 > 0 4=20 >=20 > 1 3=20 >=20 > 2 2=20 >=20 > 3 1=20 >=20 > 4 0=20 >=20 > *=20 > *=20 >=20 > This proposal defines a forbidden Merkle internal node preimage as a=20 64-byte byte string satisfying=20 > one of those five layouts and whose single output value is in the=20 consensus money range.=20 >=20 > *Specification*=20 >=20 > After activation, a block is invalid if any transaction Merkle internal= =20 node preimage encodes a=20 > minimal 64-byte transaction.=20 >=20 > For every internal Merkle parent computation in the transaction Merkle=20 tree:=20 >=20 > parent =3D SHA256d(left || right)=20 >=20 > *=20 > *=20 >=20 > where leftand rightare 32-byte child hashes, define:=20 >=20 > P =3D left || right=20 >=20 > *=20 > *=20 >=20 > The block is invalid if Psatisfies all of the following:=20 >=20 > 1.=20 >=20 > P[4] =3D=3D 0x01.=20 >=20 > 2.=20 >=20 > P[41]is one of 0, 1, 2, 3, 4.=20 >=20 > 3.=20 >=20 > Let x =3D P[41].=20 >=20 > 4.=20 >=20 > Let sequence_pos =3D 42 + x.=20 >=20 > 5.=20 >=20 > Let vout_count_pos =3D sequence_pos + 4.=20 >=20 > 6.=20 >=20 > Let value_pos =3D vout_count_pos + 1.=20 >=20 > 7.=20 >=20 > Let scriptpubkey_len_pos =3D value_pos + 8.=20 >=20 > 8.=20 >=20 > P[vout_count_pos] =3D=3D 0x01.=20 >=20 > 9.=20 >=20 > P[scriptpubkey_len_pos] =3D=3D 4 - x.=20 >=20 > 10.=20 >=20 > Let locktime_pos =3D scriptpubkey_len_pos + 1 + (4 - x).=20 >=20 > 11.=20 >=20 > locktime_pos + 4 =3D=3D 64.=20 >=20 > 12.=20 >=20 > The 8-byte little-endian integer at P[value_pos..value_pos+7]is in=20 MoneyRange.=20 >=20 > Equivalently, the forbidden preimage is a 64-byte serialization of a=20 one-input, one-output, non-=20 > witness transaction with single-byte CompactSize counts and script=20 lengths, where the two script=20 > lengths sum to 4 and the output value is in range.=20 >=20 > For clarity, "non-witness transaction" here refers to the serialization= =20 used for txidcomputation.=20 > Even for SegWit transactions, the transaction Merkle tree uses txids, so= =20 the marker byte, flag byte,=20 > and witness data are excluded.=20 >=20 > This rule applies to every transaction Merkle internal node used to=20 compute the block header's=20 > transaction Merkle root.=20 >=20 > *Odd-entry duplication*=20 >=20 > If a Merkle level has an odd number of entries, Bitcoin duplicates the=20 final hash:=20 >=20 > parent =3D SHA256d(last || last)=20 >=20 > *=20 > *=20 >=20 > The preimage:=20 >=20 > last || last=20 >=20 > *=20 > *=20 >=20 > MUST be checked by the same rule.=20 >=20 > *SPV verification rule*=20 >=20 > An SPV verifier relying on this soft fork MUST reject a Merkle proof if= =20 any branch preimage in the=20 > proof encodes a minimal 64-byte transaction under the predicate above.=20 >=20 > For each branch step, the verifier knows:=20 >=20 > 1.=20 >=20 > The current hash.=20 >=20 > 2.=20 >=20 > The sibling hash.=20 >=20 > 3.=20 >=20 > The branch direction.=20 >=20 > It reconstructs:=20 >=20 > P =3D left_child_hash || right_child_hash=20 >=20 > *=20 > *=20 >=20 > The verifier MUST check:=20 >=20 > IsForbiddenMerkleInternalNodePreimage(P) =3D=3D false=20 >=20 > *=20 > *=20 >=20 > for every branch preimage in the proof.=20 >=20 > If any branch preimage passes the forbidden-preimage predicate, the proof= =20 MUST be rejected.=20 >=20 > The verifier still performs the ordinary Merkle path computation and=20 block header proof-of-work=20 > validation.=20 >=20 > *Rationale*=20 >=20 > The known 64-byte transaction SPV malleability issue requires a byte=20 string that is both:=20 >=20 > a valid 64-byte transaction serialization=20 >=20 > *=20 > *=20 >=20 > and:=20 >=20 > a transaction Merkle internal node preimage=20 >=20 > *=20 > *=20 >=20 > This proposal forbids that overlap at the Merkle internal node boundary.= =20 >=20 > The rule is narrower than invalidating all 64-byte transactions. A=20 64-byte transaction remains valid=20 > unless its exact serialization appears as a transaction Merkle internal= =20 node preimage in the same=20 > block's transaction Merkle tree.=20 >=20 > The rule also avoids adding a general transaction validity rule that=20 exists only to protect Merkle=20 > proof semantics.=20 >=20 > *Why SegWit does not eliminate the ambiguity*=20 >=20 > It is sometimes assumed that SegWit automatically removes this ambiguity= =20 because SegWit transactions=20 > contain the marker and flag bytes:=20 >=20 > 00 01=20 >=20 > *=20 > *=20 >=20 > However, the ambiguity exists at the txidlayer, not at the=20 witness-serialization layer.=20 >=20 > The transaction Merkle root in the block header is computed from txids,= =20 and txidsare computed from=20 > the serialization that excludes:=20 >=20 > marker=20 >=20 > flag=20 >=20 > witness=20 >=20 > *=20 > *=20 >=20 > Therefore the relevant byte string remains:=20 >=20 > nVersion=20 >=20 > vin=20 >=20 > vout=20 >=20 > nLockTime=20 >=20 > *=20 > *=20 >=20 > exactly as before SegWit.=20 >=20 > The witness serialization affects the wtxid, but the block header's=20 transaction Merkle root does not=20 > commit to wtxids.=20 >=20 > As a result, the existence of the SegWit marker and flag bytes does not= =20 prevent a txidpreimage from=20 > having the same byte structure as a Merkle internal node preimage.=20 >=20 > The ambiguity addressed by this proposal therefore remains relevant in=20 the SegWit era.=20 >=20 > *Contrast with a 64-byte transaction invalidity rule*=20 >=20 > A direct alternative is:=20 >=20 > A transaction is invalid if its serialized size is exactly 64 bytes.=20 >=20 > *=20 > *=20 >=20 > That rule has several advantages:=20 >=20 > 1.=20 >=20 > It is simple to specify.=20 >=20 > 2.=20 >=20 > It is simple for SPV verifiers to implement.=20 >=20 > 3.=20 >=20 > It removes the original ambiguity by eliminating all valid 64-byte=20 transaction leaves.=20 >=20 > However, it is not correct to describe that rule as automatically fixing= =20 all light clients.=20 >=20 > A 64-byte transaction invalidity rule protects an SPV verifier only if=20 the verifier enforces the new=20 > rule when interpreting the claimed transaction. Existing or=20 application-specific SPV verifiers that=20 > merely receive a byte string and a Merkle branch may remain vulnerable if= =20 they do not parse the=20 > claimed transaction and reject exactly-64-byte transaction=20 serializations.=20 >=20 > More generally, a consensus rule invalidating 64-byte transactions does= =20 not prevent arbitrary=20 > internal node preimages from existing. It only prevents those preimages= =20 from being valid Bitcoin=20 > transactions under upgraded consensus rules. A bridge, wallet, or deposit= =20 system that accepts SPV-=20 > style proofs but performs incomplete transaction parsing may still be=20 induced to treat an internal=20 > node preimage as an application-level event.=20 >=20 > For example, suppose an application-level SPV verifier treats a proved=20 byte string as a "deposit" if=20 > some field inside the alleged transaction matches a registered deposit=20 address, deposit script, or=20 > deposit commitment, but does not fully enforce the upgraded=20 transaction-validity rule. An attacker=20 > may be able to grind child hashes so that:=20 >=20 > left_child_hash || right_child_hash=20 >=20 > *=20 > *=20 >=20 > has bytes that the application interprets as a deposit transaction or=20 deposit commitment. In some=20 > systems, the attacker may also be able to choose or register deposit data= =20 that matches bytes already=20 > present in the left-hand side of an internal node preimage.=20 >=20 > This is not a failure of upgraded full-node consensus. It is a failure of= =20 the assumption that=20 > changing full-node transaction validity automatically upgrades every SPV= =20 verifier and every bridge,=20 > wallet, or application that consumes SPV-style proofs.=20 >=20 > Therefore, both approaches require light-client changes:=20 >=20 > 64-byte transaction invalidity:=20 >=20 > Light clients must reject claimed 64-byte transaction serializations.= =20 >=20 > *=20 > *=20 >=20 > Merkle-internal-node preimage invalidity:=20 >=20 > Light clients must reject proofs containing forbidden internal branch= =20 preimages.=20 >=20 > *=20 > *=20 >=20 > The 64-byte transaction invalidity rule is simpler for light clients that= =20 correctly implement it,=20 > but it is broader at the transaction layer. This proposal places the rule= =20 at the Merkle ambiguity=20 > boundary and preserves 64-byte transactions generally.=20 >=20 > In summary:=20 >=20 > 64-byte transaction invalidity:=20 >=20 > - Simpler SPV rule when implemented correctly.=20 >=20 > - Broader transaction validity change.=20 >=20 > - Invalidates all 64-byte transactions.=20 >=20 > - Does not automatically fix SPV applications that fail to enforce the= =20 new rule.=20 >=20 > *=20 > *=20 >=20 > Merkle-internal-node preimage invalidity:=20 >=20 > - Preserves 64-byte transactions generally.=20 >=20 > - Places the rule at the Merkle ambiguity boundary.=20 >=20 > - Requires SPV verifiers to parse all branch preimages.=20 >=20 > - Directly forbids the ambiguous internal-node preimage condition.=20 >=20 > *=20 > Minimal C++ implementation sketch*=20 >=20 > This implementation checks only the minimal forbidden 64-byte shape. It= =20 does not invoke the full=20 > transaction deserializer.=20 >=20 > The function returns trueif the 64-byte preimage is forbidden.=20 >=20 > static constexpr int64_t COIN =3D 100000000;=20 >=20 > static constexpr int64_t MAX_MONEY =3D 21000000 * COIN;=20 >=20 > *=20 > *=20 >=20 > static inline bool MoneyRange(int64_t nValue)=20 >=20 > {=20 >=20 > return nValue >=3D 0 && nValue <=3D MAX_MONEY;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > static inline uint64_t ReadLE64(const unsigned char* p)=20 >=20 > {=20 >=20 > return uint64_t{p[0]}=20 >=20 > | (uint64_t{p[1]} << 8)=20 >=20 > | (uint64_t{p[2]} << 16)=20 >=20 > | (uint64_t{p[3]} << 24)=20 >=20 > | (uint64_t{p[4]} << 32)=20 >=20 > | (uint64_t{p[5]} << 40)=20 >=20 > | (uint64_t{p[6]} << 48)=20 >=20 > | (uint64_t{p[7]} << 56);=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > static bool IsForbiddenMerkleInternalNodePreimage64(const unsigned char= =20 p[64])=20 >=20 > {=20 >=20 > // Minimal 64-byte legacy transaction shape:=20 >=20 > //=20 >=20 > // 4 bytes nVersion=20 >=20 > // 1 byte vin count =3D 0x01=20 >=20 > // 36 bytes prevout=20 >=20 > // 1 byte scriptSig length =3D x=20 >=20 > // x bytes scriptSig=20 >=20 > // 4 bytes nSequence=20 >=20 > // 1 byte vout count =3D 0x01=20 >=20 > // 8 bytes nValue=20 >=20 > // 1 byte scriptPubKey length =3D y=20 >=20 > // y bytes scriptPubKey=20 >=20 > // 4 bytes nLockTime=20 >=20 > //=20 >=20 > // Since the fixed overhead is 60 bytes, x + y must equal 4.=20 >=20 > *=20 > *=20 >=20 > if (p[4] !=3D 0x01) {=20 >=20 > return false;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > const unsigned int x =3D p[41];=20 >=20 > *=20 > *=20 >=20 > switch (x) {=20 >=20 > case 0:=20 >=20 > if (p[46] !=3D 0x01) return false;=20 >=20 > if (p[55] !=3D 0x04) return false;=20 >=20 > break;=20 >=20 > *=20 > *=20 >=20 > case 1:=20 >=20 > if (p[47] !=3D 0x01) return false;=20 >=20 > if (p[56] !=3D 0x03) return false;=20 >=20 > break;=20 >=20 > *=20 > *=20 >=20 > case 2:=20 >=20 > if (p[48] !=3D 0x01) return false;=20 >=20 > if (p[57] !=3D 0x02) return false;=20 >=20 > break;=20 >=20 > *=20 > *=20 >=20 > case 3:=20 >=20 > if (p[49] !=3D 0x01) return false;=20 >=20 > if (p[58] !=3D 0x01) return false;=20 >=20 > break;=20 >=20 > *=20 > *=20 >=20 > case 4:=20 >=20 > if (p[50] !=3D 0x01) return false;=20 >=20 > if (p[59] !=3D 0x00) return false;=20 >=20 > break;=20 >=20 > *=20 > *=20 >=20 > default:=20 >=20 > return false;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > const size_t value_pos =3D 47 + x;=20 >=20 > const uint64_t raw_value =3D ReadLE64(p + value_pos);=20 >=20 > *=20 > *=20 >=20 > if (raw_value >=20 static_cast(std::numeric_limits::max())) {=20 >=20 > return false;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > const int64_t nValue =3D static_cast(raw_value);=20 >=20 > *=20 > *=20 >=20 > if (!MoneyRange(nValue)) {=20 >=20 > return false;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > return true;=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > static bool IsForbiddenMerkleInternalNode(=20 >=20 > const uint256& left,=20 >=20 > const uint256& right)=20 >=20 > {=20 >=20 > unsigned char p[64];=20 >=20 > *=20 > *=20 >=20 > std::memcpy(p, left.begin(), 32);=20 >=20 > std::memcpy(p + 32, right.begin(), 32);=20 >=20 > *=20 > *=20 >=20 > return IsForbiddenMerkleInternalNodePreimage64(p);=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > A Merkle parent computation then checks the preimage before hashing:=20 >=20 > static uint256 ComputeMerkleParentChecked(=20 >=20 > const uint256& left,=20 >=20 > const uint256& right,=20 >=20 > bool& invalid)=20 >=20 > {=20 >=20 > if (IsForbiddenMerkleInternalNode(left, right)) {=20 >=20 > invalid =3D true;=20 >=20 > return uint256{};=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > unsigned char p[64];=20 >=20 > std::memcpy(p, left.begin(), 32);=20 >=20 > std::memcpy(p + 32, right.begin(), 32);=20 >=20 > *=20 > *=20 >=20 > return Hash(Span(p, 64));=20 >=20 > }=20 >=20 > *=20 > *=20 >=20 > This is the intended minimal rule. It checks the five possible 64-byte=20 one-input, one-output=20 > transaction layouts directly.=20 >=20 > *Miner considerations*=20 >=20 > Accidental violations by honest miners are expected to be rare.=20 >=20 > Adversarial violations are possible. An attacker may grind transaction=20 identifiers so that two=20 > transactions, if placed as siblings in the transaction Merkle tree, form:= =20 >=20 > txid_A || txid_B=20 >=20 > *=20 > *=20 >=20 > which encodes a forbidden minimal 64-byte transaction.=20 >=20 > An attacker may attempt to influence sibling placement by fee rate,=20 package construction, direct=20 > miner submission, or transaction ordering effects.=20 >=20 > Therefore miners MUST check candidate block templates before mining.=20 Miners MUST NOT rely on=20 > accidental violation probability.=20 >=20 > *Merkle construction failure recovery*=20 >=20 > If a candidate block template violates this rule, the miner usually does= =20 not need to discard the=20 > entire template. The violation is local to one or more internal Merkle=20 node preimages:=20 >=20 > left_child_hash || right_child_hash=20 >=20 > *=20 > *=20 >=20 > A miner can usually repair the candidate block by changing transaction=20 order so that the offending=20 > pair of child hashes no longer appears as siblings at the violating=20 Merkle tree level.=20 >=20 > *Recommended recovery procedure*=20 >=20 > When Merkle root construction fails because an internal node preimage is= =20 forbidden, mining software=20 > SHOULD use the following procedure:=20 >=20 > 1.=20 >=20 > Record each offending internal node preimage.=20 >=20 > 2.=20 >=20 > Identify the transaction subtree contributing to each offending child=20 hash.=20 >=20 > 3.=20 >=20 > Attempt to repair the block by shuffling transaction order while=20 preserving consensus=20 > transaction-order constraints.=20 >=20 > 4.=20 >=20 > Recompute the Merkle root and re-run the internal-node preimage check.=20 >=20 > 5.=20 >=20 > If the shuffled template passes, mine the repaired template.=20 >=20 > 6.=20 >=20 > If shuffling fails repeatedly, remove one or more transactions=20 contributing to the offending=20 > subtree and rebuild the template.=20 >=20 > *Preserving transaction-order constraints*=20 >=20 > A shuffle MUST NOT violate transaction dependency ordering.=20 >=20 > If transaction Bspends an output created by transaction Ain the same=20 block, then AMUST appear before B.=20 >=20 > The coinbase transaction MUST remain the first transaction in the block.= =20 >=20 > Mining software SHOULD shuffle only transactions whose relative order is= =20 not constrained by in-block=20 > dependencies, or use a randomized topological ordering of the block's=20 transaction dependency graph.=20 >=20 > *Simple shuffle algorithm*=20 >=20 > A simple repair algorithm is:=20 >=20 > 1. Keep the coinbase fixed at index 0.=20 >=20 > 2. Build a dependency graph for all non-coinbase transactions.=20 >=20 > 3. Generate a randomized topological ordering of the graph.=20 >=20 > 4. Construct the Merkle tree using that ordering.=20 >=20 > 5. Reject the ordering if any internal node preimage is forbidden.=20 >=20 > 6. Retry with a new randomized topological ordering.=20 >=20 > *=20 > *=20 >=20 > This changes Merkle sibling relationships without violating in-block=20 transaction dependencies.=20 >=20 > *Repeated failure*=20 >=20 > If randomized repair fails repeatedly, mining software SHOULD remove=20 transactions contributing to=20 > the repeated offending subtree.=20 >=20 > A reasonable policy is:=20 >=20 > If Merkle construction fails after 2 independent shuffle attempts,=20 >=20 > remove at least one transaction from each repeatedly offending pair or=20 subtree.=20 >=20 > *=20 > *=20 >=20 > For a bottom-level violation, the offending subtree usually corresponds= =20 to two sibling transaction=20 > identifiers:=20 >=20 > txid_A || txid_B=20 >=20 > *=20 > *=20 >=20 > In that case, the miner may remove either tx_Aor tx_B.=20 >=20 > For a higher-level violation, each child hash commits to a subtree=20 containing multiple transactions.=20 > In that case, the miner may:=20 >=20 > 1. Try another dependency-preserving shuffle.=20 >=20 > 2. If the same higher-level violation recurs, remove one transaction from= =20 one child subtree.=20 >=20 > 3. Prefer removing the lowest-feerate removable transaction that does not= =20 force removal of higher-=20 > feerate descendants.=20 >=20 > *=20 > *=20 >=20 > This policy does not need to identify a malicious transaction. It only=20 needs to produce a valid=20 > block template with minimal fee loss.=20 >=20 > *Fee impact*=20 >=20 > The expected fee impact for honest block templates should be negligible= =20 because accidental=20 > violations are rare.=20 >=20 > If an adversary intentionally creates transactions that cause violations= =20 when paired, shuffling will=20 > usually defeat the attempt without fee loss. If shuffling does not repair= =20 the template, removing one=20 > or more offending transactions bounds the miner's exposure.=20 >=20 > The adversary's practical effect is limited to potentially causing some= =20 transactions to be omitted=20 > from a candidate block template. The rule prevents upgraded miners from= =20 mining invalid blocks,=20 > provided miners check the Merkle construction before mining.=20 >=20 > *Relation to unupgraded miners*=20 >=20 > Because accidental violations are rare, unupgraded miners are unlikely to= =20 encounter the rule during=20 > ordinary operation.=20 >=20 > However, an adversary can construct transaction pairs intended to trigger= =20 the rule under specific=20 > sibling placement.=20 >=20 > Unupgraded miners that do not enforce this rule may mine a block that=20 upgraded nodes reject after=20 > activation. Low accidental probability improves deployment safety but is= =20 not a substitute for miner=20 > enforcement.=20 >=20 > *Probability analysis*=20 >=20 > This section estimates accidental violation probability under simplified= =20 randomness assumptions.=20 >=20 > *Random left || right*=20 >=20 > Assume the 64-byte internal node preimage is uniformly random.=20 >=20 > For the preimage to encode a minimal one-input, one-output 64-byte=20 transaction, it must satisfy:=20 >=20 > vin_count =3D 0x01=20 >=20 > scriptSig_len =3D x, where x =E2=88=88 {0,1,2,3,4}=20 >=20 > vout_count =3D 0x01 at the position determined by x=20 >=20 > scriptPubKey_len =3D 4 - x=20 >=20 > nValue =E2=88=88 [0, MAX_MONEY]=20 >=20 > *=20 > *=20 >=20 > Ignoring nValue, the structural probability is approximately:=20 >=20 > 5 / 256^3=20 >=20 > *=20 > *=20 >=20 > because there are five valid (scriptSig_len, scriptPubKey_len)splits, and= =20 three one-byte constraints:=20 >=20 > vin_count=20 >=20 > vout_count=20 >=20 > scriptPubKey_len=20 >=20 > *=20 > *=20 >=20 > Numerically:=20 >=20 > 5 / 256^3 =E2=89=88 2.980232238769531e-7=20 >=20 > *=20 > *=20 >=20 > or approximately:=20 >=20 > 1 in 3,355,443=20 >=20 > *=20 > *=20 >=20 > Including the output value money range:=20 >=20 > MAX_MONEY =3D 21,000,000 * 100,000,000=20 >=20 > =3D 2,100,000,000,000,000=20 >=20 > *=20 > *=20 >=20 > For a uniformly random unsigned 64-bit output value, the probability of= =20 being in range is approximately:=20 >=20 > (MAX_MONEY + 1) / 2^64=20 >=20 > =E2=89=88 1.1384122811097797e-4=20 >=20 > *=20 > *=20 >=20 > Therefore the approximate probability that a random 64-byte preimage is= =20 structurally valid and has=20 > an in-range output value is:=20 >=20 > (5 / 256^3) * ((MAX_MONEY + 1) / 2^64)=20 >=20 > =E2=89=88 3.392733219831406e-11=20 >=20 > *=20 > *=20 >=20 > or approximately:=20 >=20 > 1 in 29,475,000,000=20 >=20 > *=20 > Random left || left*=20 >=20 > For an odd-entry duplicated Merkle node, the preimage has the form:=20 >=20 > left || left=20 >=20 > *=20 > *=20 >=20 > where the first 32 bytes equal the last 32 bytes.=20 >=20 > Let the 32-byte half be:=20 >=20 > A[0..31]=20 >=20 > *=20 > *=20 >=20 > Then:=20 >=20 > P[0..31] =3D A[0..31]=20 >=20 > P[32..63] =3D A[0..31]=20 >=20 > *=20 > *=20 >=20 > For the same one-input, one-output 64-byte transaction shape:=20 >=20 > P[4] =3D 0x01=20 >=20 > P[41] =3D scriptSig_len =3D x=20 >=20 > P[vout_count_pos] =3D 0x01=20 >=20 > P[scriptpubkey_len_pos] =3D 4 - x=20 >=20 > *=20 > *=20 >=20 > Because positions after byte 31 alias positions in the first half:=20 >=20 > P[i] =3D A[i mod 32]=20 >=20 > *=20 > *=20 >=20 > The relevant positions are:=20 >=20 > vin_count_pos =3D 4=20 >=20 > script_len_pos =3D 41 =E2=89=A1 9 mod 32=20 >=20 > vout_count_pos =3D 46 + x =E2=89=A1 14 + x mod 32=20 >=20 > scriptpubkey_len_pos =3D 55 + x =E2=89=A1 23 + x mod 32=20 >=20 > *=20 > *=20 >=20 > The constraints are:=20 >=20 > A[4] =3D 0x01=20 >=20 > A[9] =3D x=20 >=20 > A[14 + x] =3D 0x01=20 >=20 > A[23 + x] =3D 4 - x=20 >=20 > *=20 > *=20 >=20 > For each fixed x, these are four independent one-byte constraints under= =20 the random-half model.=20 >=20 > Thus the structural probability is approximately:=20 >=20 > 5 / 256^4=20 >=20 > =E2=89=88 1.1641532182693481e-9=20 >=20 > *=20 > *=20 >=20 > or approximately:=20 >=20 > 1 in 858,993,459=20 >=20 > *=20 > *=20 >=20 > The output value begins at:=20 >=20 > value_pos =3D 47 + x=20 >=20 > *=20 > *=20 >=20 > which aliases to an 8-byte window in the random 32-byte half:=20 >=20 > A[15 + x .. 22 + x]=20 >=20 > *=20 > *=20 >=20 > Using the same simplified independence approximation, the probability of= =20 being in MoneyRangeis=20 > approximately:=20 >=20 > (MAX_MONEY + 1) / 2^64=20 >=20 > =E2=89=88 1.1384122811097797e-4=20 >=20 > *=20 > *=20 >=20 > So the approximate probability that a random left || leftpreimage is=20 structurally valid and has an=20 > in-range output value is:=20 >=20 > (5 / 256^4) * ((MAX_MONEY + 1) / 2^64)=20 >=20 > =E2=89=88 1.3252864140005492e-13=20 >=20 > *=20 > *=20 >=20 > or approximately:=20 >=20 > 1 in 7,545,600,000,000=20 >=20 > *=20 > Block-level accidental probability*=20 >=20 > A block with ntransactions has approximately n - 1internal Merkle nodes,= =20 plus duplicated-node cases=20 > depending on tree shape.=20 >=20 > Using the rough random left || rightestimate:=20 >=20 > p =E2=89=88 3.39e-11=20 >=20 > *=20 > *=20 >=20 > A block with 10,000 transactions has approximate accidental violation=20 probability:=20 >=20 > 1 - (1 - p)^9999 =E2=89=88 3.39e-7=20 >=20 > *=20 > *=20 >=20 > or roughly:=20 >=20 > 1 in 2,950,000 blocks=20 >=20 > *=20 > *=20 >=20 > This is a simplified estimate. Actual txids are not perfect independent= =20 random samples in all cases,=20 > duplicated nodes have lower estimated probability, and additional=20 implementation details may reduce=20 > or alter the rate.=20 >=20 > The deployment-relevant conclusion is:=20 >=20 > Honest accidental violations should be rare.=20 >=20 > Adversarial violations are possible.=20 >=20 > Miners must enforce the rule.=20 >=20 > *=20 > Backward compatibility*=20 >=20 > This is a soft fork. Blocks violating the new rule were previously valid= =20 and become invalid after=20 > activation.=20 >=20 > Unupgraded full nodes may accept violating blocks after activation.=20 Activation therefore requires=20 > ordinary soft-fork deployment procedures.=20 >=20 > Unupgraded SPV clients remain vulnerable to the legacy proof ambiguity.= =20 SPV clients must update=20 > their Merkle proof validation logic to obtain the benefit of this rule.= =20 >=20 > *Test vectors*=20 >=20 > Test vectors should include:=20 >=20 > 1.=20 >=20 > A block whose transaction Merkle internal node preimages do not encode=20 minimal 64-byte=20 > transactions. The block is valid.=20 >=20 > 2.=20 >=20 > A block containing a 64-byte transaction whose serialization does not=20 appear as an internal node=20 > preimage. The block is valid.=20 >=20 > 3.=20 >=20 > A block where an internal node preimage encodes a minimal 64-byte=20 transaction. The block is invalid.=20 >=20 > 4.=20 >=20 > A block where an odd-entry duplicated preimage h || hencodes a minimal=20 64-byte transaction. The=20 > block is invalid.=20 >=20 > 5.=20 >=20 > An SPV proof where one branch preimage encodes a minimal 64-byte=20 transaction. The proof is rejected.=20 >=20 > 6.=20 >=20 > An SPV proof for a 64-byte transaction where no branch preimage encodes a= =20 minimal 64-byte=20 > transaction. The proof is accepted if otherwise valid.=20 >=20 > *Open questions*=20 >=20 > 1.=20 >=20 > Should the rule include only the explicit minimal 64-byte legacy=20 transaction shape above, or=20 > should it call the full consensus transaction deserializer?=20 >=20 > 2.=20 >=20 > Should future transaction serialization changes be required to preserve= =20 this exact forbidden-=20 > preimage invariant?=20 >=20 > 3.=20 >=20 > Should pre-activation relay policy discourage transaction pairs that can= =20 form forbidden sibling=20 > preimages?=20 >=20 > 4.=20 >=20 > Should mining software standardize a recovery procedure for failed Merkle= =20 construction, or=20 > should this remain implementation-specific?=20 >=20 > 5.=20 >=20 > Should SPV proof formats include an explicit version bit indicating=20 branch-preimage checking=20 > support?=20 >=20 > --=20 > You received this message because you are subscribed to the Google Groups= =20 "Bitcoin Development=20 > Mailing List" group.=20 > To unsubscribe from this group and stop receiving emails from it, send an= =20 email to=20 > bitcoindev+...@googlegroups.com .= =20 > To view this discussion visit=20 https://groups.google.com/d/msgid/bitcoindev/f97afcc5-54ba-4284-8e9b-=20 > e8c35c7101f6n%40googlegroups.com < https://groups.google.com/d/msgid/bitcoindev/=20 > f97afcc5-54ba-4284-8e9b-e8c35c7101f6n% 40googlegroups.com?utm_medium=3Demail&utm_source=3Dfooter>.=20 --=20 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 e= mail to bitcoindev+unsubscribe@googlegroups.com. To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/= beedd7ba-f707-40af-8557-08afad630b79n%40googlegroups.com. ------=_Part_106177_1531744813.1781544051665 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable SDL sent me a note clarifying I missed a rule to restrict the prevout index= to in-range. The updated rule should be:

The block is invalid if P satisfies all of the following:

  1. P[4] =3D=3D 0x01.

  2. P[41] is one of 0, 1, 2, 3, 4.

  3. Let x =3D P[41].

  4. Let sequence_pos =3D 42 + x<= /span>.<= /p>

  5. Let vout_count_pos =3D sequence_pos + 4.

  6. Let value_pos =3D vout_= count_pos + 1.

  7. Let scriptpubkey_len_pos =3D value_pos + 8.

  8. .

  9. P[scriptpubkey_len_pos] =3D= =3D 4 - x.

  10. Let locktime_pos =3D scriptpubkey_len_pos + 1 + (4 - x).

    =
  11. <= p dir=3D"ltr" role=3D"presentation" style=3D"line-height: 1.38; margin-top:= 0pt; margin-bottom: 0pt;">locktime_pos + 4 =3D=3D 64.

  12. The 8-byte little-end= ian integer at P[value_pos..value_pos+7] is in MoneyRange.

  13. The 4-byte little-endi= ng integer at P[4+1+32] is less than or equal to VOutRange

VOutRange is = 111,111 ( from 1e6 / (1 byte length + 8 bytes amount)).


--------

<= div>This contributes another 15.2 bits, giving approximately 51 bits of gri= nding for an invalid right hand side.

This is st= ill feasible adversarially, but unlikely to ever happen by accident (or in = deeper internal nodes).

In addition, we can also= add a policy rule to no accept or realy txns which happen to hash to this = 51 bit target shape -- this doesn't need to be consensus enforced, but prov= ides a bit of protection to legacy nodes.

------= --------

This doesn't seem to effect the adversa= rial analysis much, just makes it more expensive. If you can get a valid RH= S and LHS sent to a legacy node and predict their Txn Ordering you could ca= use a violation.


On Tuesday, = June 9, 2026 at 2:39:53=E2=80=AFPM UTC-4 Matt Corallo wrote:
Hey Jeremy,

While this is certainly an interesting alternative mitigation against= some attacks, the fact that it=20
implies that miners have to change their block-building software to h= andle potential malicious=20
transaction selection which can cause them to create invalid blocks s= eems to make this a total=20
nonstarter.

Not breaking existing deployed software, including miners who in some= cases have custom=20
block-building logic is obviously an important goal for soft forks. G= iven there's no (known) use of=20
64-byte transactions anywhere (and they've been non-standard for a lo= ngggg time!) its hard to argue=20
banning 64-byte transactions would have any similar issues. As others= have noted, banning 64-byte=20
transactions seems to be a more complete fix as well.

Matt

On 6/1/26 1:46 PM, jeremy wrote:
> Esteemed Colleagues,
>=20
> As a result of some of my research on 64-byte transactions, I'd = like to discuss an alternative soft=20
> fork proposal that preserves the ability to encode 64-byte trans= actions while offering protection to=20
> SPV users (who must make a small patch to validate the path prop= erty).
>=20
> The rule, stated simply, is:
>=20
> A block is invalid if any Merkle Tree 64-byte preimage has the e= xact byte structure of a minimal=20
> one-input, one-output, witness stripped transaction.
>=20
> [With the miracle of GPT,] I've drafted a relatively complete BI= P for discussion.
>=20
> Happy International Children's Day,
>=20
> Jeremy
>=20
> p.s. I will later propose potentially a couple other mitigations= separately, for discussion as well.
>=20
> ----------------------------------------------------------------= ------------------------------------
>=20
> BIP: TBD
> Layer: Consensus (soft fork)
> Title: Prohibit Merkle Internal Node Preimages That Encode Minim= al 64-Byte Transactions
> Author: TBD
> Status: Draft
> Type: Standards Track
> Created: 2026-06-01
> License: BSD-2-Clause
>=20
> *Abstract*
>=20
> This document specifies a consensus rule that invalidates a bloc= k if any transaction Merkle tree=20
> internal node preimage encodes a minimal 64-byte transaction.
>=20
> For each internal Merkle node, Bitcoin computes:
>=20
> parent =3D SHA256d(left || right)
>=20
> *
> *
>=20
> where leftand rightare 32-byte hashes. The 64-byte string left |= | rightis the internal node preimage.
>=20
> After activation, a block is invalid if any such 64-byte preimag= e has the exact byte structure of a=20
> minimal one-input, one-output, non-witness transaction.
>=20
> This prevents a 64-byte transaction serialization from being mal= leated into an internal Merkle node=20
> preimage in SPV transaction inclusion proofs. It does not make 6= 4-byte transactions invalid in general.
>=20
> *Motivation*
>=20
> Bitcoin transaction identifiers and transaction Merkle internal = nodes are both computed with double=20
> SHA256:
>=20
> txid =C2=A0 =3D SHA256d(serialized_transaction)
>=20
> parent =3D SHA256d(left_child_hash || right_child_hash)
>=20
> *
> *
>=20
> If a valid transaction serialization is exactly 64 bytes, the sa= me byte string can also be=20
> interpreted as the concatenation of two 32-byte Merkle child has= hes:
>=20
> serialized_transaction =3D left_child_hash || right_child_hash
>=20
> *
> *
>=20
> This creates an ambiguity between a transaction leaf preimage an= d an internal node preimage.
>=20
> An SPV verifier that accepts a Merkle proof without authenticati= ng the full tree shape can be made=20
> to accept a proof terminating at an internal node rather than at= an actual transaction leaf.
>=20
> This proposal removes that ambiguity by forbidding Merkle intern= al node preimages that have the only=20
> practical 64-byte transaction encoding shape.
>=20
> *SegWit and transaction identifiers*
>=20
> Since SegWit activation, Bitcoin transactions have two related i= dentifiers:
>=20
> txid=C2=A0 =3D SHA256d(legacy serialization)
>=20
> wtxid =3D SHA256d(witness serialization)
>=20
> *
> *
>=20
> The distinction is important for understanding this proposal.
>=20
> A SegWit transaction is serialized on the wire as:
>=20
> nVersion
>=20
> marker
>=20
> flag
>=20
> vin
>=20
> vout
>=20
> witness
>=20
> nLockTime
>=20
> *
> *
>=20
> where:
>=20
> marker =3D 0x00
>=20
> flag =C2=A0 =3D 0x01
>=20
> *
> *
>=20
> The marker and flag bytes indicate that witness data is present.
>=20
> However, the transaction identifier (txid) is not computed from = this witness serialization. Instead,=20
> the txidis computed from the legacy serialization:
>=20
> nVersion
>=20
> vin
>=20
> vout
>=20
> nLockTime
>=20
> *
> *
>=20
> with the marker, flag, and witness fields omitted.
>=20
> Therefore:
>=20
> txid =3D SHA256d(non-witness serialization)
>=20
> *
> *
>=20
> while:
>=20
> wtxid =3D SHA256d(full witness serialization)
>=20
> *
> *
>=20
> The transaction Merkle root committed in the block header is bui= lt from transaction identifiers=20
> (txids), not witness transaction identifiers (wtxids).
>=20
> Consequently:
>=20
> Merkle root =3D Merkle(txid_0, txid_1, ..., txid_n)
>=20
> *
> *
>=20
> and not:
>=20
> Merkle(wtxid_0, wtxid_1, ..., wtxid_n)
>=20
> *
> *
>=20
> This means that the marker byte (0x00), flag byte (0x01), and wi= tness data never appear in the=20
> transaction Merkle tree committed by the block header.
>=20
> SegWit does define a separate witness Merkle tree whose root is = committed through the coinbase=20
> witness commitment, but that witness Merkle tree is distinct fro= m the transaction Merkle tree=20
> discussed in this proposal.
>=20
> As a result, the ambiguity addressed by this proposal concerns o= nly transaction identifiers (txids)=20
> and the transaction Merkle root. The SegWit marker and flag byte= s are irrelevant to the transaction=20
> Merkle root because they are excluded from txidserialization.
>=20
> *Minimal 64-byte transaction shape*
>=20
> This proposal is concerned with the serialization used to comput= e a transaction's txid.
>=20
> For legacy transactions, and for SegWit transactions when comput= ing the txid, the serialization=20
> format is:
>=20
> 4 bytes =C2=A0 nVersion
>=20
> 1 byte=C2=A0 =C2=A0 vin count =3D 0x01
>=20
> 36 bytes=C2=A0 prevout
>=20
> 1 byte=C2=A0 =C2=A0 scriptSig length =3D x
>=20
> x bytes =C2=A0 scriptSig
>=20
> 4 bytes =C2=A0 nSequence
>=20
> 1 byte=C2=A0 =C2=A0 vout count =3D 0x01
>=20
> 8 bytes =C2=A0 nValue
>=20
> 1 byte=C2=A0 =C2=A0 scriptPubKey length =3D y
>=20
> y bytes =C2=A0 scriptPubKey
>=20
> 4 bytes =C2=A0 nLockTime
>=20
> *
> *
>=20
> Notably, this serialization does not include:
>=20
> marker
>=20
> flag
>=20
> witness stack
>=20
> *
> *
>=20
> because those fields are excluded from txidcomputation.
>=20
> The fixed overhead is:
>=20
> 4 + 1 + 36 + 1 + 4 + 1 + 8 + 1 + 4 =3D 60 bytes
>=20
> *
> *
>=20
> Therefore, for total serialized size 64:
>=20
> x + y =3D 4
>=20
> *
> *
>=20
> There are exactly five possible script-length splits:
>=20
> scriptSig length=C2=A0 =C2=A0 scriptPubKey length
>=20
> 0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 4
>=20
> 1 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 3
>=20
> 2 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 2
>=20
> 3 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 1
>=20
> 4 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= 0
>=20
> *
> *
>=20
> This proposal defines a forbidden Merkle internal node preimage = as a 64-byte byte string satisfying=20
> one of those five layouts and whose single output value is in th= e consensus money range.
>=20
> *Specification*
>=20
> After activation, a block is invalid if any transaction Merkle i= nternal node preimage encodes a=20
> minimal 64-byte transaction.
>=20
> For every internal Merkle parent computation in the transaction = Merkle tree:
>=20
> parent =3D SHA256d(left || right)
>=20
> *
> *
>=20
> where leftand rightare 32-byte child hashes, define:
>=20
> P =3D left || right
>=20
> *
> *
>=20
> The block is invalid if Psatisfies all of the following:
>=20
> 1.
>=20
> P[4] =3D=3D 0x01.
>=20
> 2.
>=20
> P[41]is one of 0, 1, 2, 3, 4.
>=20
> 3.
>=20
> Let x =3D P[41].
>=20
> 4.
>=20
> Let sequence_pos =3D 42 + x.
>=20
> 5.
>=20
> Let vout_count_pos =3D sequence_pos + 4.
>=20
> 6.
>=20
> Let value_pos =3D vout_count_pos + 1.
>=20
> 7.
>=20
> Let scriptpubkey_len_pos =3D value_pos + 8.
>=20
> 8.
>=20
> P[vout_count_pos] =3D=3D 0x01.
>=20
> 9.
>=20
> P[scriptpubkey_len_pos] =3D=3D 4 - x.
>=20
> 10.
>=20
> Let locktime_pos =3D scriptpubkey_len_pos + 1 + (4 - x).
>=20
> 11.
>=20
> locktime_pos + 4 =3D=3D 64.
>=20
> 12.
>=20
> The 8-byte little-endian integer at P[value_pos..value_pos+7= ]is in MoneyRange.
>=20
> Equivalently, the forbidden preimage is a 64-byte serialization = of a one-input, one-output, non-=20
> witness transaction with single-byte CompactSize counts and scri= pt lengths, where the two script=20
> lengths sum to 4 and the output value is in range.
>=20
> For clarity, "non-witness transaction" here refers to the serial= ization used for txidcomputation.=20
> Even for SegWit transactions, the transaction Merkle tree uses t= xids, so the marker byte, flag byte,=20
> and witness data are excluded.
>=20
> This rule applies to every transaction Merkle internal node used= to compute the block header's=20
> transaction Merkle root.
>=20
> *Odd-entry duplication*
>=20
> If a Merkle level has an odd number of entries, Bitcoin duplicat= es the final hash:
>=20
> parent =3D SHA256d(last || last)
>=20
> *
> *
>=20
> The preimage:
>=20
> last || last
>=20
> *
> *
>=20
> MUST be checked by the same rule.
>=20
> *SPV verification rule*
>=20
> An SPV verifier relying on this soft fork MUST reject a Merkle p= roof if any branch preimage in the=20
> proof encodes a minimal 64-byte transaction under the predicate = above.
>=20
> For each branch step, the verifier knows:
>=20
> 1.
>=20
> The current hash.
>=20
> 2.
>=20
> The sibling hash.
>=20
> 3.
>=20
> The branch direction.
>=20
> It reconstructs:
>=20
> P =3D left_child_hash || right_child_hash
>=20
> *
> *
>=20
> The verifier MUST check:
>=20
> IsForbiddenMerkleInternalNodePreimage(P) =3D=3D false
>=20
> *
> *
>=20
> for every branch preimage in the proof.
>=20
> If any branch preimage passes the forbidden-preimage predicate, = the proof MUST be rejected.
>=20
> The verifier still performs the ordinary Merkle path computation= and block header proof-of-work=20
> validation.
>=20
> *Rationale*
>=20
> The known 64-byte transaction SPV malleability issue requires a = byte string that is both:
>=20
> a valid 64-byte transaction serialization
>=20
> *
> *
>=20
> and:
>=20
> a transaction Merkle internal node preimage
>=20
> *
> *
>=20
> This proposal forbids that overlap at the Merkle internal node b= oundary.
>=20
> The rule is narrower than invalidating all 64-byte transactions.= A 64-byte transaction remains valid=20
> unless its exact serialization appears as a transaction Merkle i= nternal node preimage in the same=20
> block's transaction Merkle tree.
>=20
> The rule also avoids adding a general transaction validity rule = that exists only to protect Merkle=20
> proof semantics.
>=20
> *Why SegWit does not eliminate the ambiguity*
>=20
> It is sometimes assumed that SegWit automatically removes this a= mbiguity because SegWit transactions=20
> contain the marker and flag bytes:
>=20
> 00 01
>=20
> *
> *
>=20
> However, the ambiguity exists at the txidlayer, not at the witne= ss-serialization layer.
>=20
> The transaction Merkle root in the block header is computed from= txids, and txidsare computed from=20
> the serialization that excludes:
>=20
> marker
>=20
> flag
>=20
> witness
>=20
> *
> *
>=20
> Therefore the relevant byte string remains:
>=20
> nVersion
>=20
> vin
>=20
> vout
>=20
> nLockTime
>=20
> *
> *
>=20
> exactly as before SegWit.
>=20
> The witness serialization affects the wtxid, but the block heade= r's transaction Merkle root does not=20
> commit to wtxids.
>=20
> As a result, the existence of the SegWit marker and flag bytes d= oes not prevent a txidpreimage from=20
> having the same byte structure as a Merkle internal node preimag= e.
>=20
> The ambiguity addressed by this proposal therefore remains relev= ant in the SegWit era.
>=20
> *Contrast with a 64-byte transaction invalidity rule*
>=20
> A direct alternative is:
>=20
> A transaction is invalid if its serialized size is exactly 64 by= tes.
>=20
> *
> *
>=20
> That rule has several advantages:
>=20
> 1.
>=20
> It is simple to specify.
>=20
> 2.
>=20
> It is simple for SPV verifiers to implement.
>=20
> 3.
>=20
> It removes the original ambiguity by eliminating all valid 6= 4-byte transaction leaves.
>=20
> However, it is not correct to describe that rule as automaticall= y fixing all light clients.
>=20
> A 64-byte transaction invalidity rule protects an SPV verifier o= nly if the verifier enforces the new=20
> rule when interpreting the claimed transaction. Existing or appl= ication-specific SPV verifiers that=20
> merely receive a byte string and a Merkle branch may remain vuln= erable if they do not parse the=20
> claimed transaction and reject exactly-64-byte transaction seria= lizations.
>=20
> More generally, a consensus rule invalidating 64-byte transactio= ns does not prevent arbitrary=20
> internal node preimages from existing. It only prevents those pr= eimages from being valid Bitcoin=20
> transactions under upgraded consensus rules. A bridge, wallet, o= r deposit system that accepts SPV-=20
> style proofs but performs incomplete transaction parsing may sti= ll be induced to treat an internal=20
> node preimage as an application-level event.
>=20
> For example, suppose an application-level SPV verifier treats a = proved byte string as a "deposit" if=20
> some field inside the alleged transaction matches a registered d= eposit address, deposit script, or=20
> deposit commitment, but does not fully enforce the upgraded tran= saction-validity rule. An attacker=20
> may be able to grind child hashes so that:
>=20
> left_child_hash || right_child_hash
>=20
> *
> *
>=20
> has bytes that the application interprets as a deposit transacti= on or deposit commitment. In some=20
> systems, the attacker may also be able to choose or register dep= osit data that matches bytes already=20
> present in the left-hand side of an internal node preimage.
>=20
> This is not a failure of upgraded full-node consensus. It is a f= ailure of the assumption that=20
> changing full-node transaction validity automatically upgrades e= very SPV verifier and every bridge,=20
> wallet, or application that consumes SPV-style proofs.
>=20
> Therefore, both approaches require light-client changes:
>=20
> 64-byte transaction invalidity:
>=20
> =C2=A0=C2=A0Light clients must reject claimed 64-byte transacti= on serializations.
>=20
> *
> *
>=20
> Merkle-internal-node preimage invalidity:
>=20
> =C2=A0=C2=A0Light clients must reject proofs containing forbidd= en internal branch preimages.
>=20
> *
> *
>=20
> The 64-byte transaction invalidity rule is simpler for light cli= ents that correctly implement it,=20
> but it is broader at the transaction layer. This proposal places= the rule at the Merkle ambiguity=20
> boundary and preserves 64-byte transactions generally.
>=20
> In summary:
>=20
> 64-byte transaction invalidity:
>=20
> =C2=A0=C2=A0- Simpler SPV rule when implemented correctly.
>=20
> =C2=A0=C2=A0- Broader transaction validity change.
>=20
> =C2=A0=C2=A0- Invalidates all 64-byte transactions.
>=20
> =C2=A0=C2=A0- Does not automatically fix SPV applications that = fail to enforce the new rule.
>=20
> *
> *
>=20
> Merkle-internal-node preimage invalidity:
>=20
> =C2=A0=C2=A0- Preserves 64-byte transactions generally.
>=20
> =C2=A0=C2=A0- Places the rule at the Merkle ambiguity boundary.
>=20
> =C2=A0=C2=A0- Requires SPV verifiers to parse all branch preima= ges.
>=20
> =C2=A0=C2=A0- Directly forbids the ambiguous internal-node prei= mage condition.
>=20
> *
> Minimal C++ implementation sketch*
>=20
> This implementation checks only the minimal forbidden 64-byte sh= ape. It does not invoke the full=20
> transaction deserializer.
>=20
> The function returns trueif the 64-byte preimage is forbidden.
>=20
> static constexpr int64_t COIN =3D 100000000;
>=20
> static constexpr int64_t MAX_MONEY =3D 21000000 * COIN;
>=20
> *
> *
>=20
> static inline bool MoneyRange(int64_t nValue)
>=20
> {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0return nValue >=3D 0 && nVal= ue <=3D MAX_MONEY;
>=20
> }
>=20
> *
> *
>=20
> static inline uint64_t ReadLE64(const unsigned char* p)
>=20
> {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0return uint64_t{p[0]}
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[1= ]} << 8)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[2= ]} << 16)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[3= ]} << 24)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[4= ]} << 32)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[5= ]} << 40)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[6= ]} << 48)
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[7= ]} << 56);
>=20
> }
>=20
> *
> *
>=20
> static bool IsForbiddenMerkleInternalNodePreimage64(const unsign= ed char p[64])
>=20
> {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// Minimal 64-byte legacy transaction s= hape:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0//
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =C2=A0 nVersion
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=C2=A0 =C2=A0 vin count= =3D 0x01
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 36 bytes=C2=A0 prevout
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=C2=A0 =C2=A0 scriptSig= length =3D x
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 x bytes =C2=A0 scriptSig
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =C2=A0 nSequence
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=C2=A0 =C2=A0 vout coun= t =3D 0x01
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 8 bytes =C2=A0 nValue
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=C2=A0 =C2=A0 scriptPub= Key length =3D y
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 y bytes =C2=A0 scriptPubKey
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =C2=A0 nLockTime
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0//
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0// Since the fixed overhead is 60 bytes= , x + y must equal 4.
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0if (p[4] !=3D 0x01) {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0}
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const unsigned int x =3D p[41];
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0switch (x) {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0case 0:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[46] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[55] !=3D = 0x04) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0case 1:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[47] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[56] !=3D = 0x03) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0case 2:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[48] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[57] !=3D = 0x02) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0case 3:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[49] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[58] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0case 4:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[50] !=3D = 0x01) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[59] !=3D = 0x00) return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0default:
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0}
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const size_t value_pos =3D 47 + x;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const uint64_t raw_value =3D ReadLE64(p= + value_pos);
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0if (raw_value > static_cast<uint6= 4_t>(std::numeric_limits<int64_t>::max())) {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0}
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const int64_t nValue =3D static_cast<= ;int64_t>(raw_value);
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0if (!MoneyRange(nValue)) {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0}
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0return true;
>=20
> }
>=20
> *
> *
>=20
> static bool IsForbiddenMerkleInternalNode(
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const uint256& left,
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const uint256& right)
>=20
> {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0unsigned char p[64];
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p, left.begin(), 32);
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p + 32, right.begin(), 32);
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0return IsForbiddenMerkleInternalNodePre= image64(p);
>=20
> }
>=20
> *
> *
>=20
> A Merkle parent computation then checks the preimage before hash= ing:
>=20
> static uint256 ComputeMerkleParentChecked(
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const uint256& left,
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0const uint256& right,
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0bool& invalid)
>=20
> {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0if (IsForbiddenMerkleInternalNode(left,= right)) {
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0invalid =3D tru= e;
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return uint256{= };
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0}
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0unsigned char p[64];
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p, left.begin(), 32);
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p + 32, right.begin(), 32);
>=20
> *
> *
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0return Hash(Span<const unsigned char= >(p, 64));
>=20
> }
>=20
> *
> *
>=20
> This is the intended minimal rule. It checks the five possible 6= 4-byte one-input, one-output=20
> transaction layouts directly.
>=20
> *Miner considerations*
>=20
> Accidental violations by honest miners are expected to be rare.
>=20
> Adversarial violations are possible. An attacker may grind trans= action identifiers so that two=20
> transactions, if placed as siblings in the transaction Merkle tr= ee, form:
>=20
> txid_A || txid_B
>=20
> *
> *
>=20
> which encodes a forbidden minimal 64-byte transaction.
>=20
> An attacker may attempt to influence sibling placement by fee ra= te, package construction, direct=20
> miner submission, or transaction ordering effects.
>=20
> Therefore miners MUST check candidate block templates before min= ing. Miners MUST NOT rely on=20
> accidental violation probability.
>=20
> *Merkle construction failure recovery*
>=20
> If a candidate block template violates this rule, the miner usua= lly does not need to discard the=20
> entire template. The violation is local to one or more internal = Merkle node preimages:
>=20
> left_child_hash || right_child_hash
>=20
> *
> *
>=20
> A miner can usually repair the candidate block by changing trans= action order so that the offending=20
> pair of child hashes no longer appears as siblings at the violat= ing Merkle tree level.
>=20
> *Recommended recovery procedure*
>=20
> When Merkle root construction fails because an internal node pre= image is forbidden, mining software=20
> SHOULD use the following procedure:
>=20
> 1.
>=20
> Record each offending internal node preimage.
>=20
> 2.
>=20
> Identify the transaction subtree contributing to each offend= ing child hash.
>=20
> 3.
>=20
> Attempt to repair the block by shuffling transaction order w= hile preserving consensus
> transaction-order constraints.
>=20
> 4.
>=20
> Recompute the Merkle root and re-run the internal-node preim= age check.
>=20
> 5.
>=20
> If the shuffled template passes, mine the repaired template.
>=20
> 6.
>=20
> If shuffling fails repeatedly, remove one or more transactio= ns contributing to the offending
> subtree and rebuild the template.
>=20
> *Preserving transaction-order constraints*
>=20
> A shuffle MUST NOT violate transaction dependency ordering.
>=20
> If transaction Bspends an output created by transaction Ain the = same block, then AMUST appear before B.
>=20
> The coinbase transaction MUST remain the first transaction in th= e block.
>=20
> Mining software SHOULD shuffle only transactions whose relative = order is not constrained by in-block=20
> dependencies, or use a randomized topological ordering of the bl= ock's transaction dependency graph.
>=20
> *Simple shuffle algorithm*
>=20
> A simple repair algorithm is:
>=20
> 1. Keep the coinbase fixed at index 0.
>=20
> 2. Build a dependency graph for all non-coinbase transactions.
>=20
> 3. Generate a randomized topological ordering of the graph.
>=20
> 4. Construct the Merkle tree using that ordering.
>=20
> 5. Reject the ordering if any internal node preimage is forbidde= n.
>=20
> 6. Retry with a new randomized topological ordering.
>=20
> *
> *
>=20
> This changes Merkle sibling relationships without violating in-b= lock transaction dependencies.
>=20
> *Repeated failure*
>=20
> If randomized repair fails repeatedly, mining software SHOULD re= move transactions contributing to=20
> the repeated offending subtree.
>=20
> A reasonable policy is:
>=20
> If Merkle construction fails after 2 independent shuffle attempt= s,
>=20
> remove at least one transaction from each repeatedly offending p= air or subtree.
>=20
> *
> *
>=20
> For a bottom-level violation, the offending subtree usually corr= esponds to two sibling transaction=20
> identifiers:
>=20
> txid_A || txid_B
>=20
> *
> *
>=20
> In that case, the miner may remove either tx_Aor tx_B.
>=20
> For a higher-level violation, each child hash commits to a subtr= ee containing multiple transactions.=20
> In that case, the miner may:
>=20
> 1. Try another dependency-preserving shuffle.
>=20
> 2. If the same higher-level violation recurs, remove one transac= tion from one child subtree.
>=20
> 3. Prefer removing the lowest-feerate removable transaction that= does not force removal of higher-=20
> feerate descendants.
>=20
> *
> *
>=20
> This policy does not need to identify a malicious transaction. I= t only needs to produce a valid=20
> block template with minimal fee loss.
>=20
> *Fee impact*
>=20
> The expected fee impact for honest block templates should be neg= ligible because accidental=20
> violations are rare.
>=20
> If an adversary intentionally creates transactions that cause vi= olations when paired, shuffling will=20
> usually defeat the attempt without fee loss. If shuffling does n= ot repair the template, removing one=20
> or more offending transactions bounds the miner's exposure.
>=20
> The adversary's practical effect is limited to potentially causi= ng some transactions to be omitted=20
> from a candidate block template. The rule prevents upgraded mine= rs from mining invalid blocks,=20
> provided miners check the Merkle construction before mining.
>=20
> *Relation to unupgraded miners*
>=20
> Because accidental violations are rare, unupgraded miners are un= likely to encounter the rule during=20
> ordinary operation.
>=20
> However, an adversary can construct transaction pairs intended t= o trigger the rule under specific=20
> sibling placement.
>=20
> Unupgraded miners that do not enforce this rule may mine a block= that upgraded nodes reject after=20
> activation. Low accidental probability improves deployment safet= y but is not a substitute for miner=20
> enforcement.
>=20
> *Probability analysis*
>=20
> This section estimates accidental violation probability under si= mplified randomness assumptions.
>=20
> *Random left || right*
>=20
> Assume the 64-byte internal node preimage is uniformly random.
>=20
> For the preimage to encode a minimal one-input, one-output 64-by= te transaction, it must satisfy:
>=20
> vin_count =3D 0x01
>=20
> scriptSig_len =3D x, where x =E2=88=88 {0,1,2,3,4}
>=20
> vout_count =3D 0x01 at the position determined by x
>=20
> scriptPubKey_len =3D 4 - x
>=20
> nValue =E2=88=88 [0, MAX_MONEY]
>=20
> *
> *
>=20
> Ignoring nValue, the structural probability is approximately:
>=20
> 5 / 256^3
>=20
> *
> *
>=20
> because there are five valid (scriptSig_len, scriptPubKey_len)sp= lits, and three one-byte constraints:
>=20
> vin_count
>=20
> vout_count
>=20
> scriptPubKey_len
>=20
> *
> *
>=20
> Numerically:
>=20
> 5 / 256^3 =E2=89=88 2.980232238769531e-7
>=20
> *
> *
>=20
> or approximately:
>=20
> 1 in 3,355,443
>=20
> *
> *
>=20
> Including the output value money range:
>=20
> MAX_MONEY =3D 21,000,000 * 100,000,000
>=20
> =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=3D= 2,100,000,000,000,000
>=20
> *
> *
>=20
> For a uniformly random unsigned 64-bit output value, the probabi= lity of being in range is approximately:
>=20
> (MAX_MONEY + 1) / 2^64
>=20
> =E2=89=88 1.1384122811097797e-4
>=20
> *
> *
>=20
> Therefore the approximate probability that a random 64-byte prei= mage is structurally valid and has=20
> an in-range output value is:
>=20
> (5 / 256^3) * ((MAX_MONEY + 1) / 2^64)
>=20
> =E2=89=88 3.392733219831406e-11
>=20
> *
> *
>=20
> or approximately:
>=20
> 1 in 29,475,000,000
>=20
> *
> Random left || left*
>=20
> For an odd-entry duplicated Merkle node, the preimage has the fo= rm:
>=20
> left || left
>=20
> *
> *
>=20
> where the first 32 bytes equal the last 32 bytes.
>=20
> Let the 32-byte half be:
>=20
> A[0..31]
>=20
> *
> *
>=20
> Then:
>=20
> P[0..31]=C2=A0 =3D A[0..31]
>=20
> P[32..63] =3D A[0..31]
>=20
> *
> *
>=20
> For the same one-input, one-output 64-byte transaction shape:
>=20
> P[4]=C2=A0 =3D 0x01
>=20
> P[41] =3D scriptSig_len =3D x
>=20
> P[vout_count_pos] =3D 0x01
>=20
> P[scriptpubkey_len_pos] =3D 4 - x
>=20
> *
> *
>=20
> Because positions after byte 31 alias positions in the first hal= f:
>=20
> P[i] =3D A[i mod 32]
>=20
> *
> *
>=20
> The relevant positions are:
>=20
> vin_count_pos=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D 4
>=20
> script_len_pos =C2=A0 =C2=A0 =C2=A0 =3D 41=C2=A0 =C2=A0 =C2=A0 = =E2=89=A1 9=C2=A0 mod 32
>=20
> vout_count_pos =C2=A0 =C2=A0 =C2=A0 =3D 46 + x=C2=A0 =E2=89=A1 1= 4 + x mod 32
>=20
> scriptpubkey_len_pos =3D 55 + x=C2=A0 =E2=89=A1 23 + x mod 32
>=20
> *
> *
>=20
> The constraints are:
>=20
> A[4]=C2=A0 =C2=A0 =C2=A0 =3D 0x01
>=20
> A[9]=C2=A0 =C2=A0 =C2=A0 =3D x
>=20
> A[14 + x] =3D 0x01
>=20
> A[23 + x] =3D 4 - x
>=20
> *
> *
>=20
> For each fixed x, these are four independent one-byte constraint= s under the random-half model.
>=20
> Thus the structural probability is approximately:
>=20
> 5 / 256^4
>=20
> =E2=89=88 1.1641532182693481e-9
>=20
> *
> *
>=20
> or approximately:
>=20
> 1 in 858,993,459
>=20
> *
> *
>=20
> The output value begins at:
>=20
> value_pos =3D 47 + x
>=20
> *
> *
>=20
> which aliases to an 8-byte window in the random 32-byte half:
>=20
> A[15 + x .. 22 + x]
>=20
> *
> *
>=20
> Using the same simplified independence approximation, the probab= ility of being in MoneyRangeis=20
> approximately:
>=20
> (MAX_MONEY + 1) / 2^64
>=20
> =E2=89=88 1.1384122811097797e-4
>=20
> *
> *
>=20
> So the approximate probability that a random left || leftpreimag= e is structurally valid and has an=20
> in-range output value is:
>=20
> (5 / 256^4) * ((MAX_MONEY + 1) / 2^64)
>=20
> =E2=89=88 1.3252864140005492e-13
>=20
> *
> *
>=20
> or approximately:
>=20
> 1 in 7,545,600,000,000
>=20
> *
> Block-level accidental probability*
>=20
> A block with ntransactions has approximately n - 1internal Merkl= e nodes, plus duplicated-node cases=20
> depending on tree shape.
>=20
> Using the rough random left || rightestimate:
>=20
> p =E2=89=88 3.39e-11
>=20
> *
> *
>=20
> A block with 10,000 transactions has approximate accidental viol= ation probability:
>=20
> 1 - (1 - p)^9999 =E2=89=88 3.39e-7
>=20
> *
> *
>=20
> or roughly:
>=20
> 1 in 2,950,000 blocks
>=20
> *
> *
>=20
> This is a simplified estimate. Actual txids are not perfect inde= pendent random samples in all cases,=20
> duplicated nodes have lower estimated probability, and additiona= l implementation details may reduce=20
> or alter the rate.
>=20
> The deployment-relevant conclusion is:
>=20
> Honest accidental violations should be rare.
>=20
> Adversarial violations are possible.
>=20
> Miners must enforce the rule.
>=20
> *
> Backward compatibility*
>=20
> This is a soft fork. Blocks violating the new rule were previous= ly valid and become invalid after=20
> activation.
>=20
> Unupgraded full nodes may accept violating blocks after activati= on. Activation therefore requires=20
> ordinary soft-fork deployment procedures.
>=20
> Unupgraded SPV clients remain vulnerable to the legacy proof amb= iguity. SPV clients must update=20
> their Merkle proof validation logic to obtain the benefit of thi= s rule.
>=20
> *Test vectors*
>=20
> Test vectors should include:
>=20
> 1.
>=20
> A block whose transaction Merkle internal node preimages do = not encode minimal 64-byte
> transactions. The block is valid.
>=20
> 2.
>=20
> A block containing a 64-byte transaction whose serialization= does not appear as an internal node
> preimage. The block is valid.
>=20
> 3.
>=20
> A block where an internal node preimage encodes a minimal 64= -byte transaction. The block is invalid.
>=20
> 4.
>=20
> A block where an odd-entry duplicated preimage h || hencodes= a minimal 64-byte transaction. The
> block is invalid.
>=20
> 5.
>=20
> An SPV proof where one branch preimage encodes a minimal 64-= byte transaction. The proof is rejected.
>=20
> 6.
>=20
> An SPV proof for a 64-byte transaction where no branch preim= age encodes a minimal 64-byte
> transaction. The proof is accepted if otherwise valid.
>=20
> *Open questions*
>=20
> 1.
>=20
> Should the rule include only the explicit minimal 64-byte le= gacy transaction shape above, or
> should it call the full consensus transaction deserializer?
>=20
> 2.
>=20
> Should future transaction serialization changes be required = to preserve this exact forbidden-
> preimage invariant?
>=20
> 3.
>=20
> Should pre-activation relay policy discourage transaction pa= irs that can form forbidden sibling
> preimages?
>=20
> 4.
>=20
> Should mining software standardize a recovery procedure for = failed Merkle construction, or
> should this remain implementation-specific?
>=20
> 5.
>=20
> Should SPV proof formats include an explicit version bit ind= icating branch-preimage checking
> support?
>=20
> --=20
> You received this message because you are subscribed to the Goog= le Groups "Bitcoin Development=20
> Mailing List" group.
> To unsubscribe from this group and stop receiving emails from it= , send an email to=20
> bitcoindev+...@googlegroups.com <mailto:bitcoindev+...@googlegroups.com<= /a>>.
> To view this discussion visit
https://groups.google.com/d/msgid/bitcoindev/f97afcc5-54ba-4284-8e9= b-=20
> e8c35c7101f6n%40googlegroups.com <https:/= /groups.google.com/d/msgid/bitcoindev/=20
> f97afcc5-54ba-4284-8e9b-e8c35c7101f6n%40googlegroups.com?utm_medium=3Demail&utm_source=3Dfoote= r>.

--
You received this message because you are subscribed to the Google Groups &= quot;Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an e= mail to bitcoind= ev+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoind= ev/beedd7ba-f707-40af-8557-08afad630b79n%40googlegroups.com.
------=_Part_106177_1531744813.1781544051665-- ------=_Part_106176_1697668896.1781544051665--