From mboxrd@z Thu Jan 1 00:00:00 1970
Delivery-date: Mon, 01 Jun 2026 11:03:01 -0700
Received: from mail-oo1-f61.google.com ([209.85.161.61])
by mail.fairlystable.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
(Exim 4.94.2)
(envelope-from )
id 1wU6yY-0003ME-H3
for bitcoindev@gnusha.org; Mon, 01 Jun 2026 11:03:01 -0700
Received: by mail-oo1-f61.google.com with SMTP id 006d021491bc7-69dc9cb3663sf8196504eaf.1
for ; Mon, 01 Jun 2026 11:02:58 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=googlegroups.com; s=20251104; t=1780336972; x=1780941772; 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:message-id:to:from:date:sender:from:to:cc:subject:date
:message-id:reply-to;
bh=YJOHPJcitcePwzbKnrD++XfzHrug5YZpGKZwQDtpXgk=;
b=mP8FWHxfbRtlxZx66kCZMmZdJdXduHS7On7E6OlNXs6KELt1Z3xd5ChX4QOwT4iP4F
30nRzvQWvsNBR8ztfJ0ytrEJHdF0gsq7OA5n96LHn6s3VzzU+RHFOYy16Yqo/mp4ShVg
3fvWlg+dqqv6NErlDtMWhwgCeDLvyaPrqz1/zMJdRjtkJcq5Xy3yyyGVOrEW5MjszfEn
76N6jfCOYTpcCZt1exguscdT3V449zDwQYeMNwGArATz/ZmPUyq872hdjDQjK7+k2+uG
3HCtSY1LCw/ko8d/4md+qqleTZTh0Ml4DH6a5HBHbXBIh8AgCHoBpslWYcshkMt3Y7T9
690Q==
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=gmail.com; s=20251104; t=1780336972; x=1780941772; 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:message-id:to:from:date:from:to:cc:subject:date:message-id
:reply-to;
bh=YJOHPJcitcePwzbKnrD++XfzHrug5YZpGKZwQDtpXgk=;
b=FIxOHRPCgg80QUAZ0jsHBLSONUV4uusgSAy8Gplu81YIT7MYPlI7yvA5kPYG2wYHHf
jUf763+WSQGuubpzlZTkftrq7zteU2yVAXxJxqcRCDfSph66hg8Tsl0eCIYcG3/0TGLF
06H7VjwSsgglTZWnXfkM1hAjMvmJS1oeIWz/A4u8/iW4oWhXV15ZtclKWxHTKblyb3R0
6iGUX3oapyQ15Xpkvd5+vo8pAvCT3Kbk6yPLgGeHvSgQP73acK7FEUDnYmKp6bSp9Dgh
CMQCMJ0lXIx78Y2VpY5f4G2hB3ghC4c7N+a1BxUlmC60Oi6GyI3L+qTjVH5yt4BA5Ot3
23rA==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=1e100.net; s=20251104; t=1780336972; x=1780941772;
h=list-unsubscribe:list-subscribe:list-archive:list-help:list-post
:list-id:mailing-list:precedence:x-original-sender:mime-version
:subject:message-id:to:from:date:x-beenthere:x-gm-message-state
:sender:from:to:cc:subject:date:message-id:reply-to;
bh=YJOHPJcitcePwzbKnrD++XfzHrug5YZpGKZwQDtpXgk=;
b=TXledpf63kkQG56kVxIlbVWzlLgd0uMATaCV7IvI3nD1nPvXt6SRfUWFRqJtC3+vEZ
Lf4g9SRwNluIDZ4gl2jWljKmxK2jcUiQvHTO6081mkywovzEQ1Hqif6o2kYNdrJGdt36
QukA3/+bq1gFNxwLzSaT4dD4CER7eQdmMC8aun3zDaYMck7F0Kd15rJJeUaA7gpzElAb
JuhpnfDj6HNPztsph3fix+AzwaPwCKdXjmiD7K5ZlQUpWv3WBWgpXoZN1mohGzbBS9WV
mGTNomhYQefMIuF5T/wZotum6SXmxoV/tr3NWaK63OgiAJgzY0wisJOkY1HQ20u9FSD0
2SXQ==
Sender: bitcoindev@googlegroups.com
X-Forwarded-Encrypted: i=1; AFNElJ88Nt0ORn7OREYhO5Pl1HM+FeP9wIktT21QXKxAfWDyJwdk2o/e1uhWfmoGcksj20yIVrTTHOQmLUSu@gnusha.org
X-Gm-Message-State: AOJu0Yxi8SIynhTJxoSYzxcXkCMzm2wp0D4ZSVFbWnUK+oZSX7OgM5Nj
pUwClvpWx8VekAzWga2Rv96TB20IHewAJ2gZsjy9i8Y7cNyUs7QjfYpJ
X-Received: by 2002:a05:6820:c8a:b0:69e:327f:f048 with SMTP id 006d021491bc7-69e32800993mr1075584eaf.56.1780336971006;
Mon, 01 Jun 2026 11:02:51 -0700 (PDT)
X-BeenThere: bitcoindev@googlegroups.com; h="AUV6zMM2xIMT22pvEtG03dGDJbQqspMtC933wqR0UFlfvRp0bQ=="
Received: by 2002:a05:6820:2081:b0:67b:f5b9:99d8 with SMTP id
006d021491bc7-69df41f8ad9ls2814367eaf.0.-pod-prod-02-us; Mon, 01 Jun 2026
11:02:43 -0700 (PDT)
X-Received: by 2002:a05:6808:5094:b0:479:eb19:6e6a with SMTP id 5614622812f47-485fbcc64c6mr8324361b6e.34.1780336963468;
Mon, 01 Jun 2026 11:02:43 -0700 (PDT)
Received: by 2002:a05:690c:5688:b0:7ba:f5aa:4ab8 with SMTP id 00721157ae682-7dc2f3656ccms7b3;
Mon, 1 Jun 2026 10:46:42 -0700 (PDT)
X-Received: by 2002:a05:690c:7006:b0:7b4:6f40:635 with SMTP id 00721157ae682-7e05cb40f54mr109250067b3.16.1780336000848;
Mon, 01 Jun 2026 10:46:40 -0700 (PDT)
Date: Mon, 1 Jun 2026 10:46:40 -0700 (PDT)
From: jeremy
To: Bitcoin Development Mailing List
Message-Id:
Subject: [bitcoindev] Prohibit Merkle Internal Node Preimages That Encode
Minimal 64-Byte Transactions
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_Part_487476_1918830888.1780336000367"
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_487476_1918830888.1780336000367
Content-Type: multipart/alternative;
boundary="----=_Part_487477_1559598651.1780336000367"
------=_Part_487477_1559598651.1780336000367
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Esteemed Colleagues,
As a result of some of my research on 64-byte transactions, I'd like to=20
discuss an alternative soft fork proposal that preserves the ability to=20
encode 64-byte transactions while offering protection to SPV users (who=20
must make a small patch to validate the path property).
The rule, stated simply, is:
A block is invalid if any Merkle Tree 64-byte preimage has the exact byte=
=20
structure of a minimal one-input, one-output, witness stripped transaction.
[With the miracle of GPT,] I've drafted a relatively complete BIP for=20
discussion.
Happy International Children's Day,
Jeremy
p.s. I will later propose potentially a couple other mitigations=20
separately, for discussion as well.
------------------------------
BIP: TBD
Layer: Consensus (soft fork)
Title: Prohibit Merkle Internal Node Preimages That Encode Minimal 64-Byte=
=20
Transactions
Author: TBD
Status: Draft
Type: Standards Track
Created: 2026-06-01
License: BSD-2-Clause
*Abstract*
This document specifies a consensus rule that invalidates a block if any=20
transaction Merkle tree internal node preimage encodes a minimal 64-byte=20
transaction.
For each internal Merkle node, Bitcoin computes:
parent =3D SHA256d(left || right)
where left and right are 32-byte hashes. The 64-byte string left || right=
=20
is the internal node preimage.
After activation, a block is invalid if any such 64-byte preimage has the=
=20
exact byte structure of a minimal one-input, one-output, non-witness=20
transaction.
This prevents a 64-byte transaction serialization from being malleated into=
=20
an internal Merkle node preimage in SPV transaction inclusion proofs. It=20
does not make 64-byte transactions invalid in general.
*Motivation*
Bitcoin transaction identifiers and transaction Merkle internal nodes are=
=20
both computed with double SHA256:
txid =3D SHA256d(serialized_transaction)
parent =3D SHA256d(left_child_hash || right_child_hash)
If a valid transaction serialization is exactly 64 bytes, the same byte=20
string can also be interpreted as the concatenation of two 32-byte Merkle=
=20
child hashes:
serialized_transaction =3D left_child_hash || right_child_hash
This creates an ambiguity between a transaction leaf preimage and an=20
internal node preimage.
An SPV verifier that accepts a Merkle proof without authenticating the full=
=20
tree shape can be made to accept a proof terminating at an internal node=20
rather than at an actual transaction leaf.
This proposal removes that ambiguity by forbidding Merkle internal node=20
preimages that have the only practical 64-byte transaction encoding shape.
*SegWit and transaction identifiers*
Since SegWit activation, Bitcoin transactions have two related identifiers:
txid =3D SHA256d(legacy serialization)
wtxid =3D SHA256d(witness serialization)
The distinction is important for understanding this proposal.
A SegWit transaction is serialized on the wire as:
nVersion
marker
flag
vin
vout
witness
nLockTime
where:
marker =3D 0x00
flag =3D 0x01
The marker and flag bytes indicate that witness data is present.
However, the transaction identifier (txid) is not computed from this=20
witness serialization. Instead, the txid is computed from the legacy=20
serialization:
nVersion
vin
vout
nLockTime
with the marker, flag, and witness fields omitted.
Therefore:
txid =3D SHA256d(non-witness serialization)
while:
wtxid =3D SHA256d(full witness serialization)
The transaction Merkle root committed in the block header is built from=20
transaction identifiers (txids), not witness transaction identifiers (wtxid=
s
).
Consequently:
Merkle root =3D Merkle(txid_0, txid_1, ..., txid_n)
and not:
Merkle(wtxid_0, wtxid_1, ..., wtxid_n)
This means that the marker byte (0x00), flag byte (0x01), and witness data=
=20
never appear in the transaction Merkle tree committed by the block header.
SegWit does define a separate witness Merkle tree whose root is committed=
=20
through the coinbase witness commitment, but that witness Merkle tree is=20
distinct from the transaction Merkle tree discussed in this proposal.
As a result, the ambiguity addressed by this proposal concerns only=20
transaction identifiers (txids) and the transaction Merkle root. The SegWit=
=20
marker and flag bytes are irrelevant to the transaction Merkle root because=
=20
they are excluded from txid serialization.
*Minimal 64-byte transaction shape*
This proposal is concerned with the serialization used to compute a=20
transaction's txid.
For legacy transactions, and for SegWit transactions when computing the txi=
d,=20
the serialization format is:
4 bytes nVersion
1 byte vin count =3D 0x01
36 bytes prevout
1 byte scriptSig length =3D x
x bytes scriptSig
4 bytes nSequence
1 byte vout count =3D 0x01
8 bytes nValue
1 byte scriptPubKey length =3D y
y bytes scriptPubKey
4 bytes nLockTime
Notably, this serialization does not include:
marker
flag
witness stack
because those fields are excluded from txid computation.
The fixed overhead is:
4 + 1 + 36 + 1 + 4 + 1 + 8 + 1 + 4 =3D 60 bytes
Therefore, for total serialized size 64:
x + y =3D 4
There are exactly five possible script-length splits:
scriptSig length scriptPubKey length
0 4
1 3
2 2
3 1
4 0
This proposal defines a forbidden Merkle internal node preimage as a=20
64-byte byte string satisfying one of those five layouts and whose single=
=20
output value is in the consensus money range.
*Specification*
After activation, a block is invalid if any transaction Merkle internal=20
node preimage encodes a minimal 64-byte transaction.
For every internal Merkle parent computation in the transaction Merkle tree=
:
parent =3D SHA256d(left || right)
where left and right are 32-byte child hashes, define:
P =3D left || right
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.
=20
Equivalently, the forbidden preimage is a 64-byte serialization of a=20
one-input, one-output, non-witness transaction with single-byte CompactSize=
=20
counts and script lengths, where the two script lengths sum to 4 and the=20
output value is in range.
For clarity, "non-witness transaction" here refers to the serialization=20
used for txid computation. Even for SegWit transactions, the transaction=20
Merkle tree uses txids, so the marker byte, flag byte, and witness data are=
=20
excluded.
This rule applies to every transaction Merkle internal node used to compute=
=20
the block header's transaction Merkle root.
*Odd-entry duplication*
If a Merkle level has an odd number of entries, Bitcoin duplicates the=20
final hash:
parent =3D SHA256d(last || last)
The preimage:
last || last
MUST be checked by the same rule.
*SPV verification rule*
An SPV verifier relying on this soft fork MUST reject a Merkle proof if any=
=20
branch preimage in the proof encodes a minimal 64-byte transaction under=20
the predicate above.
For each branch step, the verifier knows:
1.=20
=20
The current hash.
2.=20
=20
The sibling hash.
3.=20
=20
The branch direction.
=20
It reconstructs:
P =3D left_child_hash || right_child_hash
The verifier MUST check:
IsForbiddenMerkleInternalNodePreimage(P) =3D=3D false
for every branch preimage in the proof.
If any branch preimage passes the forbidden-preimage predicate, the proof=
=20
MUST be rejected.
The verifier still performs the ordinary Merkle path computation and block=
=20
header proof-of-work validation.
*Rationale*
The known 64-byte transaction SPV malleability issue requires a byte string=
=20
that is both:
a valid 64-byte transaction serialization
and:
a transaction Merkle internal node preimage
This proposal forbids that overlap at the Merkle internal node boundary.
The rule is narrower than invalidating all 64-byte transactions. A 64-byte=
=20
transaction remains valid unless its exact serialization appears as a=20
transaction Merkle internal node preimage in the same block's transaction=
=20
Merkle tree.
The rule also avoids adding a general transaction validity rule that exists=
=20
only to protect Merkle proof semantics.
*Why SegWit does not eliminate the ambiguity*
It is sometimes assumed that SegWit automatically removes this ambiguity=20
because SegWit transactions contain the marker and flag bytes:
00 01
However, the ambiguity exists at the txid layer, not at the=20
witness-serialization layer.
The transaction Merkle root in the block header is computed from txids, and=
=20
txids are computed from the serialization that excludes:
marker
flag
witness
Therefore the relevant byte string remains:
nVersion
vin
vout
nLockTime
exactly as before SegWit.
The witness serialization affects the wtxid, but the block header's=20
transaction Merkle root does not commit to wtxids.
As a result, the existence of the SegWit marker and flag bytes does not=20
prevent a txid preimage from having the same byte structure as a Merkle=20
internal node preimage.
The ambiguity addressed by this proposal therefore remains relevant in the=
=20
SegWit era.
*Contrast with a 64-byte transaction invalidity rule*
A direct alternative is:
A transaction is invalid if its serialized size is exactly 64 bytes.
That rule has several advantages:
1.=20
=20
It is simple to specify.
2.=20
=20
It is simple for SPV verifiers to implement.
3.=20
=20
It removes the original ambiguity by eliminating all valid 64-byte=20
transaction leaves.
=20
However, it is not correct to describe that rule as automatically fixing=20
all light clients.
A 64-byte transaction invalidity rule protects an SPV verifier only if the=
=20
verifier enforces the new rule when interpreting the claimed transaction.=
=20
Existing or application-specific SPV verifiers that merely receive a byte=
=20
string and a Merkle branch may remain vulnerable if they do not parse the=
=20
claimed transaction and reject exactly-64-byte transaction serializations.
More generally, a consensus rule invalidating 64-byte transactions does not=
=20
prevent arbitrary internal node preimages from existing. It only prevents=
=20
those preimages from being valid Bitcoin transactions under upgraded=20
consensus rules. A bridge, wallet, or deposit system that accepts SPV-style=
=20
proofs but performs incomplete transaction parsing may still be induced to=
=20
treat an internal node preimage as an application-level event.
For example, suppose an application-level SPV verifier treats a proved byte=
=20
string as a "deposit" if some field inside the alleged transaction matches=
=20
a registered deposit address, deposit script, or deposit commitment, but=20
does not fully enforce the upgraded transaction-validity rule. An attacker=
=20
may be able to grind child hashes so that:
left_child_hash || right_child_hash
has bytes that the application interprets as a deposit transaction or=20
deposit commitment. In some systems, the attacker may also be able to=20
choose or register deposit data that matches bytes already present in the=
=20
left-hand side of an internal node preimage.
This is not a failure of upgraded full-node consensus. It is a failure of=
=20
the assumption that changing full-node transaction validity automatically=
=20
upgrades every SPV verifier and every bridge, wallet, or application that=
=20
consumes SPV-style proofs.
Therefore, both approaches require light-client changes:
64-byte transaction invalidity:
Light clients must reject claimed 64-byte transaction serializations.
Merkle-internal-node preimage invalidity:
Light clients must reject proofs containing forbidden internal branch=20
preimages.
The 64-byte transaction invalidity rule is simpler for light clients that=
=20
correctly implement it, but it is broader at the transaction layer. This=20
proposal places the rule at the Merkle ambiguity boundary and preserves=20
64-byte transactions generally.
In summary:
64-byte transaction invalidity:
- Simpler SPV rule when implemented correctly.
- Broader transaction validity change.
- Invalidates all 64-byte transactions.
- Does not automatically fix SPV applications that fail to enforce the=20
new rule.
Merkle-internal-node preimage invalidity:
- Preserves 64-byte transactions generally.
- Places the rule at the Merkle ambiguity boundary.
- Requires SPV verifiers to parse all branch preimages.
- Directly forbids the ambiguous internal-node preimage condition.
*Minimal C++ implementation sketch*
This implementation checks only the minimal forbidden 64-byte shape. It=20
does not invoke the full transaction deserializer.
The function returns true if the 64-byte preimage is forbidden.
static constexpr int64_t COIN =3D 100000000;
static constexpr int64_t MAX_MONEY =3D 21000000 * COIN;
static inline bool MoneyRange(int64_t nValue)
{
return nValue >=3D 0 && nValue <=3D MAX_MONEY;
}
static inline uint64_t ReadLE64(const unsigned char* p)
{
return uint64_t{p[0]}
| (uint64_t{p[1]} << 8)
| (uint64_t{p[2]} << 16)
| (uint64_t{p[3]} << 24)
| (uint64_t{p[4]} << 32)
| (uint64_t{p[5]} << 40)
| (uint64_t{p[6]} << 48)
| (uint64_t{p[7]} << 56);
}
static bool IsForbiddenMerkleInternalNodePreimage64(const unsigned char=20
p[64])
{
// Minimal 64-byte legacy transaction shape:
//
// 4 bytes nVersion
// 1 byte vin count =3D 0x01
// 36 bytes prevout
// 1 byte scriptSig length =3D x
// x bytes scriptSig
// 4 bytes nSequence
// 1 byte vout count =3D 0x01
// 8 bytes nValue
// 1 byte scriptPubKey length =3D y
// y bytes scriptPubKey
// 4 bytes nLockTime
//
// Since the fixed overhead is 60 bytes, x + y must equal 4.
if (p[4] !=3D 0x01) {
return false;
}
const unsigned int x =3D p[41];
switch (x) {
case 0:
if (p[46] !=3D 0x01) return false;
if (p[55] !=3D 0x04) return false;
break;
case 1:
if (p[47] !=3D 0x01) return false;
if (p[56] !=3D 0x03) return false;
break;
case 2:
if (p[48] !=3D 0x01) return false;
if (p[57] !=3D 0x02) return false;
break;
case 3:
if (p[49] !=3D 0x01) return false;
if (p[58] !=3D 0x01) return false;
break;
case 4:
if (p[50] !=3D 0x01) return false;
if (p[59] !=3D 0x00) return false;
break;
default:
return false;
}
const size_t value_pos =3D 47 + x;
const uint64_t raw_value =3D ReadLE64(p + value_pos);
if (raw_value >=20
static_cast(std::numeric_limits::max())) {
return false;
}
const int64_t nValue =3D static_cast(raw_value);
if (!MoneyRange(nValue)) {
return false;
}
return true;
}
static bool IsForbiddenMerkleInternalNode(
const uint256& left,
const uint256& right)
{
unsigned char p[64];
std::memcpy(p, left.begin(), 32);
std::memcpy(p + 32, right.begin(), 32);
return IsForbiddenMerkleInternalNodePreimage64(p);
}
A Merkle parent computation then checks the preimage before hashing:
static uint256 ComputeMerkleParentChecked(
const uint256& left,
const uint256& right,
bool& invalid)
{
if (IsForbiddenMerkleInternalNode(left, right)) {
invalid =3D true;
return uint256{};
}
unsigned char p[64];
std::memcpy(p, left.begin(), 32);
std::memcpy(p + 32, right.begin(), 32);
return Hash(Span(p, 64));
}
This is the intended minimal rule. It checks the five possible 64-byte=20
one-input, one-output transaction layouts directly.
*Miner considerations*
Accidental violations by honest miners are expected to be rare.
Adversarial violations are possible. An attacker may grind transaction=20
identifiers so that two transactions, if placed as siblings in the=20
transaction Merkle tree, form:
txid_A || txid_B
which encodes a forbidden minimal 64-byte transaction.
An attacker may attempt to influence sibling placement by fee rate, package=
=20
construction, direct miner submission, or transaction ordering effects.
Therefore miners MUST check candidate block templates before mining. Miners=
=20
MUST NOT rely on accidental violation probability.
*Merkle construction failure recovery*
If a candidate block template violates this rule, the miner usually does=20
not need to discard the entire template. The violation is local to one or=
=20
more internal Merkle node preimages:
left_child_hash || right_child_hash
A miner can usually repair the candidate block by changing transaction=20
order so that the offending pair of child hashes no longer appears as=20
siblings at the violating Merkle tree level.
*Recommended recovery procedure*
When Merkle root construction fails because an internal node preimage is=20
forbidden, mining software SHOULD use the following procedure:
1.=20
=20
Record each offending internal node preimage.
2.=20
=20
Identify the transaction subtree contributing to each offending child=20
hash.
3.=20
=20
Attempt to repair the block by shuffling transaction order while=20
preserving consensus transaction-order constraints.
4.=20
=20
Recompute the Merkle root and re-run the internal-node preimage check.
5.=20
=20
If the shuffled template passes, mine the repaired template.
6.=20
=20
If shuffling fails repeatedly, remove one or more transactions=20
contributing to the offending subtree and rebuild the template.
=20
*Preserving transaction-order constraints*
A shuffle MUST NOT violate transaction dependency ordering.
If transaction B spends an output created by transaction A in the same=20
block, then A MUST appear before B.
The coinbase transaction MUST remain the first transaction in the block.
Mining software SHOULD shuffle only transactions whose relative order is=20
not constrained by in-block dependencies, or use a randomized topological=
=20
ordering of the block's transaction dependency graph.
*Simple shuffle algorithm*
A simple repair algorithm is:
1. Keep the coinbase fixed at index 0.
2. Build a dependency graph for all non-coinbase transactions.
3. Generate a randomized topological ordering of the graph.
4. Construct the Merkle tree using that ordering.
5. Reject the ordering if any internal node preimage is forbidden.
6. Retry with a new randomized topological ordering.
This changes Merkle sibling relationships without violating in-block=20
transaction dependencies.
*Repeated failure*
If randomized repair fails repeatedly, mining software SHOULD remove=20
transactions contributing to the repeated offending subtree.
A reasonable policy is:
If Merkle construction fails after 2 independent shuffle attempts,
remove at least one transaction from each repeatedly offending pair or=20
subtree.
For a bottom-level violation, the offending subtree usually corresponds to=
=20
two sibling transaction identifiers:
txid_A || txid_B
In that case, the miner may remove either tx_A or tx_B.
For a higher-level violation, each child hash commits to a subtree=20
containing multiple transactions. In that case, the miner may:
1. Try another dependency-preserving shuffle.
2. If the same higher-level violation recurs, remove one transaction from=
=20
one child subtree.
3. Prefer removing the lowest-feerate removable transaction that does not=
=20
force removal of higher-feerate descendants.
This policy does not need to identify a malicious transaction. It only=20
needs to produce a valid block template with minimal fee loss.
*Fee impact*
The expected fee impact for honest block templates should be negligible=20
because accidental violations are rare.
If an adversary intentionally creates transactions that cause violations=20
when paired, shuffling will usually defeat the attempt without fee loss. If=
=20
shuffling does not repair the template, removing one or more offending=20
transactions bounds the miner's exposure.
The adversary's practical effect is limited to potentially causing some=20
transactions to be omitted from a candidate block template. The rule=20
prevents upgraded miners from mining invalid blocks, provided miners check=
=20
the Merkle construction before mining.
*Relation to unupgraded miners*
Because accidental violations are rare, unupgraded miners are unlikely to=
=20
encounter the rule during ordinary operation.
However, an adversary can construct transaction pairs intended to trigger=
=20
the rule under specific sibling placement.
Unupgraded miners that do not enforce this rule may mine a block that=20
upgraded nodes reject after activation. Low accidental probability improves=
=20
deployment safety but is not a substitute for miner enforcement.
*Probability analysis*
This section estimates accidental violation probability under simplified=20
randomness assumptions.
*Random left || right*
Assume the 64-byte internal node preimage is uniformly random.
For the preimage to encode a minimal one-input, one-output 64-byte=20
transaction, it must satisfy:
vin_count =3D 0x01
scriptSig_len =3D x, where x =E2=88=88 {0,1,2,3,4}
vout_count =3D 0x01 at the position determined by x
scriptPubKey_len =3D 4 - x
nValue =E2=88=88 [0, MAX_MONEY]
Ignoring nValue, the structural probability is approximately:
5 / 256^3
because there are five valid (scriptSig_len, scriptPubKey_len) splits, and=
=20
three one-byte constraints:
vin_count
vout_count
scriptPubKey_len
Numerically:
5 / 256^3 =E2=89=88 2.980232238769531e-7
or approximately:
1 in 3,355,443
Including the output value money range:
MAX_MONEY =3D 21,000,000 * 100,000,000
=3D 2,100,000,000,000,000
For a uniformly random unsigned 64-bit output value, the probability of=20
being in range is approximately:
(MAX_MONEY + 1) / 2^64
=E2=89=88 1.1384122811097797e-4
Therefore the approximate probability that a random 64-byte preimage is=20
structurally valid and has an in-range output value is:
(5 / 256^3) * ((MAX_MONEY + 1) / 2^64)
=E2=89=88 3.392733219831406e-11
or approximately:
1 in 29,475,000,000
*Random left || left*
For an odd-entry duplicated Merkle node, the preimage has the form:
left || left
where the first 32 bytes equal the last 32 bytes.
Let the 32-byte half be:
A[0..31]
Then:
P[0..31] =3D A[0..31]
P[32..63] =3D A[0..31]
For the same one-input, one-output 64-byte transaction shape:
P[4] =3D 0x01
P[41] =3D scriptSig_len =3D x
P[vout_count_pos] =3D 0x01
P[scriptpubkey_len_pos] =3D 4 - x
Because positions after byte 31 alias positions in the first half:
P[i] =3D A[i mod 32]
The relevant positions are:
vin_count_pos =3D 4
script_len_pos =3D 41 =E2=89=A1 9 mod 32
vout_count_pos =3D 46 + x =E2=89=A1 14 + x mod 32
scriptpubkey_len_pos =3D 55 + x =E2=89=A1 23 + x mod 32
The constraints are:
A[4] =3D 0x01
A[9] =3D x
A[14 + x] =3D 0x01
A[23 + x] =3D 4 - x
For each fixed x, these are four independent one-byte constraints under the=
=20
random-half model.
Thus the structural probability is approximately:
5 / 256^4
=E2=89=88 1.1641532182693481e-9
or approximately:
1 in 858,993,459
The output value begins at:
value_pos =3D 47 + x
which aliases to an 8-byte window in the random 32-byte half:
A[15 + x .. 22 + x]
Using the same simplified independence approximation, the probability of=20
being in MoneyRange is approximately:
(MAX_MONEY + 1) / 2^64
=E2=89=88 1.1384122811097797e-4
So the approximate probability that a random left || left preimage is=20
structurally valid and has an in-range output value is:
(5 / 256^4) * ((MAX_MONEY + 1) / 2^64)
=E2=89=88 1.3252864140005492e-13
or approximately:
1 in 7,545,600,000,000
*Block-level accidental probability*
A block with n transactions has approximately n - 1 internal Merkle nodes,=
=20
plus duplicated-node cases depending on tree shape.
Using the rough random left || right estimate:
p =E2=89=88 3.39e-11
A block with 10,000 transactions has approximate accidental violation=20
probability:
1 - (1 - p)^9999 =E2=89=88 3.39e-7
or roughly:
1 in 2,950,000 blocks
This is a simplified estimate. Actual txids are not perfect independent=20
random samples in all cases, duplicated nodes have lower estimated=20
probability, and additional implementation details may reduce or alter the=
=20
rate.
The deployment-relevant conclusion is:
Honest accidental violations should be rare.
Adversarial violations are possible.
Miners must enforce the rule.
*Backward compatibility*
This is a soft fork. Blocks violating the new rule were previously valid=20
and become invalid after activation.
Unupgraded full nodes may accept violating blocks after activation.=20
Activation therefore requires ordinary soft-fork deployment procedures.
Unupgraded SPV clients remain vulnerable to the legacy proof ambiguity. SPV=
=20
clients must update their Merkle proof validation logic to obtain the=20
benefit of this rule.
*Test vectors*
Test vectors should include:
1.=20
=20
A block whose transaction Merkle internal node preimages do not encode=
=20
minimal 64-byte transactions. The block is valid.
2.=20
=20
A block containing a 64-byte transaction whose serialization does not=20
appear as an internal node preimage. The block is valid.
3.=20
=20
A block where an internal node preimage encodes a minimal 64-byte=20
transaction. The block is invalid.
4.=20
=20
A block where an odd-entry duplicated preimage h || h encodes a minimal=
=20
64-byte transaction. The block is invalid.
5.=20
=20
An SPV proof where one branch preimage encodes a minimal 64-byte=20
transaction. The proof is rejected.
6.=20
=20
An SPV proof for a 64-byte transaction where no branch preimage encodes=
=20
a minimal 64-byte transaction. The proof is accepted if otherwise valid.
=20
*Open questions*
1.=20
=20
Should the rule include only the explicit minimal 64-byte legacy=20
transaction shape above, or should it call the full consensus transactio=
n=20
deserializer?
2.=20
=20
Should future transaction serialization changes be required to preserve=
=20
this exact forbidden-preimage invariant?
3.=20
=20
Should pre-activation relay policy discourage transaction pairs that can=
=20
form forbidden sibling preimages?
4.=20
=20
Should mining software standardize a recovery procedure for failed=20
Merkle construction, or should this remain implementation-specific?
5.=20
=20
Should SPV proof formats include an explicit version bit indicating=20
branch-preimage checking support?
=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/=
f97afcc5-54ba-4284-8e9b-e8c35c7101f6n%40googlegroups.com.
------=_Part_487477_1559598651.1780336000367
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable
Esteemed Colleagues,
As a result of some of my research on 64-byte transacti=
ons, I'd like to discuss an alternative soft fork proposal that preserves t=
he ability to encode 64-byte transactions while offering protection to SPV =
users (who must make a small patch to validate the path property).=
p>The rule, stated simply, is:
A block is invalid if any Merkle Tree 64-byte preimage has the ex=
act byte structure of a minimal one-input, one-output, witness stripped tra=
nsaction.
[With the miracle of GP=
T,] I've drafted a relatively complete BIP for discussion.
Happy International Children's Day,
Jeremy
p.s. I will later pro=
pose potentially a couple other mitigations separately, for discussion as w=
ell.
BIP: TBD
Layer: Cons=
ensus (soft fork)
Title: Prohibit Merkle Internal Node Preimages That Encode Minima=
l 64-Byte Transactions
Author: TBD
Status: Draft
Type: Standards Track
Created: 2026-06-01
License: BSD-2-Clause
Abstract
This docu=
ment specifies a consensus rule that invalidates a block if any transaction=
Merkle tree internal node preimage encodes a minimal 64-byte transaction.<=
/span>
For each internal Merkle node, Bi=
tcoin computes:
parent =3D SHA256d(=
left || right)
where left and right are =
32-byte hashes. The 64-byte string left || right is the internal no=
de preimage.
After activation, a =
block is invalid if any such 64-byte preimage has the exact byte structure =
of a minimal one-input, one-output, non-witness transaction.
This prevents a 64-byte transaction serializati=
on from being malleated into an internal Merkle node preimage in SPV transa=
ction inclusion proofs. It does not make 64-byte transactions invalid in ge=
neral.
Motivation
Bitcoin transaction identifiers and transaction Merkle internal nodes are=
both computed with double SHA256:
=
txid =C2=A0 =3D SHA256d(serialized_transaction)
parent =3D SHA256d(left_child_hash || right_child_hash)=
If a valid transaction serialization is exactly 64 bytes, the =
same byte string can also be interpreted as the concatenation of two 32-byt=
e Merkle child hashes:
serialized_t=
ransaction =3D left_child_hash || right_child_hash
This c=
reates an ambiguity between a transaction leaf preimage and an internal nod=
e preimage.
An SPV verifier that =
accepts a Merkle proof without authenticating the full tree shape can be ma=
de to accept a proof terminating at an internal node rather than at an actu=
al transaction leaf.
This proposa=
l removes that ambiguity by forbidding Merkle internal node preimages that =
have the only practical 64-byte transaction encoding shape.
SegWit an=
d transaction identifiers
Since SegWit activation, Bitcoin transactions have two related identifi=
ers:
txid=C2=A0 =3D SHA256d(legacy =
serialization)
wtxid =3D SHA256d(wi=
tness serialization)
<=
/b>
The distinction is important for und=
erstanding this proposal.
A SegWi=
t transaction is serialized on the wire as:
nVersion
marker
flag
vin
vout
witness
nLockTime
where:
marker =3D 0=
x00
flag =C2=A0 =3D 0x01
=
The marker and flag bytes indicate that witness data is present.
However, the transaction identifier=
(txid) is not computed from this witness serialization. Instead, t=
he txid is computed from the legacy serialization:
=
nVersion
vi=
n
vout
nLockTime
with the marker, flag, and witness=
fields omitted.
Therefore:
txid =3D SHA256d(non-witness serializati=
on)
while:
wtxid =
=3D SHA256d(full witness serialization)
The transaction M=
erkle root committed in the block header is built from transaction identifi=
ers (txids), not witness transaction identifiers (wtxids).<=
/span>
Consequently:
Merkle root =3D Merkle(txid_0, txid_1, ..., txid_n)
and not:
Merkle(wtx=
id_0, wtxid_1, ..., wtxid_n)
This means that the marker b=
yte (0x00), flag byte (0x01), and witness data never appear=
in the transaction Merkle tree committed by the block header.
SegWit does define a separate witness Merkle =
tree whose root is committed through the coinbase witness commitment, but t=
hat witness Merkle tree is distinct from the transaction Merkle tree discus=
sed in this proposal.
As a result=
, the ambiguity addressed by this proposal concerns only transaction identi=
fiers (txids) and the transaction Merkle root. The SegWit marker an=
d flag bytes are irrelevant to the transaction Merkle root because they are=
excluded from txid serialization.
Minimal 64-byte transactio=
n shape
This proposal =
is concerned with the serialization used to compute a transaction's =
txid.
For legacy transactions, a=
nd for SegWit transactions when computing the txid, the serializati=
on format is:
4 bytes =C2=A0 nVersi=
on
1 byte=C2=A0 =C2=A0 vin count =
=3D 0x01
36 bytes=C2=A0 prevout
1 byte=C2=A0 =C2=A0 scriptSig length =
=3D x
x bytes =C2=A0 scriptSig
4 bytes =C2=A0 nSequence
1 byte=C2=A0 =C2=A0 vout count =3D 0x01
8 bytes =C2=A0 nValue
1 byte=C2=A0 =C2=A0 scriptPubKey length =3D y
<=
p dir=3D"ltr" style=3D"line-height: 1.38; margin-top: 0pt; margin-bottom: 0=
pt;">y bytes =C2=A0 scriptPubKey
The current hash.
The sibling hash.
<=
/li>The branch direction.
<=
/ol>It reconstructs:
P =3D left_child_hash || right_child_hash
The verifier MUST check:
IsForbi=
ddenMerkleInternalNodePreimage(P) =3D=3D false
for every =
branch preimage in the proof.
If =
any branch preimage passes the forbidden-preimage predicate, the proof MUST=
be rejected.
The verifier still =
performs the ordinary Merkle path computation and block header proof-of-wor=
k validation.
Rationale
The known 64-byte transaction SPV malleability issue requires a byt=
e string that is both:
a valid 64-b=
yte transaction serialization
and:
a transaction Merkle internal node preimage
=
This proposal forbids that overlap at the Merkle internal node boundar=
y.
The rule is narrower than inva=
lidating all 64-byte transactions. A 64-byte transaction remains valid unle=
ss its exact serialization appears as a transaction Merkle internal node pr=
eimage in the same block's transaction Merkle tree.
The rule also avoids adding a general transaction validi=
ty rule that exists only to protect Merkle proof semantics.
Why SegWi=
t does not eliminate the ambiguity
It is sometimes assumed that SegWit automatically removes this=
ambiguity because SegWit transactions contain the marker and flag bytes:=
span>
00 01
However, the=
ambiguity exists at the txid layer, not at the witness-serializati=
on layer.
The transaction Merkle =
root in the block header is computed from txids, and txids =
are computed from the serialization that excludes:
marker
flag<=
/p>
witness
Therefore the re=
levant byte string remains:
nVersio=
n
vin
vout
nLockTime<=
/p>
exactly as before SegWit.
The witness serialization affects the wtxid, but the block he=
ader's transaction Merkle root does not commit to wtxids.
As a result, the existence of the SegWit m=
arker and flag bytes does not prevent a txid preimage from having t=
he same byte structure as a Merkle internal node preimage.
The ambiguity addressed by this proposal therefor=
e remains relevant in the SegWit era.
Contrast with a 64-byte transac=
tion invalidity rule
A=
direct alternative is:
A transacti=
on is invalid if its serialized size is exactly 64 bytes.
That rule has several advantages:
It is simple to specify.
It is simple for SPV verifiers to implement.
It removes the original ambiguity by eliminating all =
valid 64-byte transaction leaves.
However, it is not correct to describe that rule as automatica=
lly fixing all light clients.
A 6=
4-byte transaction invalidity rule protects an SPV verifier only if the ver=
ifier enforces the new rule when interpreting the claimed transaction. Exis=
ting or application-specific SPV verifiers that merely receive a byte strin=
g and a Merkle branch may remain vulnerable if they do not parse the claime=
d transaction and reject exactly-64-byte transaction serializations.=
More generally, a consensus rule invali=
dating 64-byte transactions does not prevent arbitrary internal node preima=
ges from existing. It only prevents those preimages from being valid Bitcoi=
n transactions under upgraded consensus rules. A bridge, wallet, or deposit=
system that accepts SPV-style proofs but performs incomplete transaction p=
arsing may still be induced to treat an internal node preimage as an applic=
ation-level event.
For example, s=
uppose an application-level SPV verifier treats a proved byte string as a "=
deposit" if some field inside the alleged transaction matches a registered =
deposit address, deposit script, or deposit commitment, but does not fully =
enforce the upgraded transaction-validity rule. An attacker may be able to =
grind child hashes so that:
left_ch=
ild_hash || right_child_hash
has bytes that the applicati=
on interprets as a deposit transaction or deposit commitment. In some syste=
ms, the attacker may also be able to choose or register deposit data that m=
atches bytes already present in the left-hand side of an internal node prei=
mage.
This is not a failure of up=
graded full-node consensus. It is a failure of the assumption that changing=
full-node transaction validity automatically upgrades every SPV verifier a=
nd every bridge, wallet, or application that consumes SPV-style proofs.
Therefore, both approaches require l=
ight-client changes:
64-byte transa=
ction invalidity:
=C2=A0=C2=A0Light=
clients must reject claimed 64-byte transaction serializations.
=
Merkle-internal-node preimage invalidity:
=C2=A0=C2=A0Light clients must reject proofs containing forbid=
den internal branch preimages.
The 64-byte transaction in=
validity rule is simpler for light clients that correctly implement it, but=
it is broader at the transaction layer. This proposal places the rule at t=
he Merkle ambiguity boundary and preserves 64-byte transactions generally.<=
/span>
In summary:
64-byte transaction invalidity:
=C2=A0=C2=A0- Simpler SPV rule when implemented correctly.<=
/span>
=C2=A0=C2=A0- Broader transaction v=
alidity change.
=C2=A0=C2=A0- Inval=
idates all 64-byte transactions.
=
=C2=A0=C2=A0- Does not automatically fix SPV applications that fail to enfo=
rce the new rule.
=
Merkle-internal-node preimage invalidity:=
=C2=A0=C2=A0- Preserves 64-byte tr=
ansactions generally.
=C2=A0=C2=A0-=
Places the rule at the Merkle ambiguity boundary.
=C2=A0=C2=A0- Requires SPV verifiers to parse all branch pr=
eimages.
=C2=A0=C2=A0- Directly for=
bids the ambiguous internal-node preimage condition.
Minimal C=
++ implementation sketch
This implementation checks only the minimal forbidden 64-byte shape. It =
does not invoke the full transaction deserializer.
The function returns true if the 64-byte preimage=
is forbidden.
static constexpr int=
64_t COIN =3D 100000000;
static con=
stexpr int64_t MAX_MONEY =3D 21000000 * COIN;
static inline=
bool MoneyRange(int64_t nValue)
{<=
/span>
=C2=A0=C2=A0=C2=A0=C2=A0return nVal=
ue >=3D 0 && nValue <=3D MAX_MONEY;
}
<=
/p>
static inline uint64_t ReadLE64(const unsi=
gned char* p)
{
=C2=A0=C2=A0=C2=A0=C2=A0return uint64_t{p[0]}=
p>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0| (uint64_t{p[1]} << 8)
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[2]} <<=
16)
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0| (uint64_t{p[3]} << 24)
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p=
[4]} << 32)
=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[5]} << 40)
<=
p dir=3D"ltr" style=3D"line-height: 1.38; margin-top: 0pt; margin-bottom: 0=
pt;">=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0| (uint64_t{p[6]} << 48)
=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0| (uint64_t{p[7]} << =
56);
}
static bool =
IsForbiddenMerkleInternalNodePreimage64(const unsigned char p[64])=
p>
{
=
=C2=A0=C2=A0=C2=A0=C2=A0// Minimal 64-byte legacy transaction shape:=
=C2=A0=C2=A0=C2=A0=C2=A0//
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =C2=A0 =
nVersion
=C2=A0=C2=A0=C2=A0=C2=A0//=
=C2=A0 1 byte=C2=A0 =C2=A0 vin count =3D 0x01
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 36 bytes=C2=A0 prevout
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=
=C2=A0 =C2=A0 scriptSig length =3D x
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 x bytes =C2=A0 scriptSig
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =C2=A0 =
nSequence
=C2=A0=C2=A0=C2=A0=C2=A0/=
/ =C2=A0 1 byte=C2=A0 =C2=A0 vout count =3D 0x01
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 8 bytes =C2=A0 nValue
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 1 byte=
=C2=A0 =C2=A0 scriptPubKey length =3D y
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 y bytes =C2=A0 scriptPubKey=
p>
=C2=A0=C2=A0=C2=A0=C2=A0// =C2=A0 4 bytes =
=C2=A0 nLockTime
=C2=A0=C2=A0=C2=A0=
=C2=A0//
=C2=A0=C2=A0=C2=A0=C2=A0//=
Since the fixed overhead is 60 bytes, x + y must equal 4.
=
=C2=A0=C2=A0=C2=A0=C2=A0if (p[4] !=3D 0x01) {
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;<=
/span>
=C2=A0=C2=A0=C2=A0=C2=A0}
=C2=A0=C2=A0=C2=A0=C2=A0const unsigned int x =3D p[41];
=C2=A0=C2=A0=C2=A0=C2=A0switch (x) {
=C2=A0=C2=A0=C2=A0=C2=A0case 0:
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[46] !=3D 0x01) ret=
urn false;
=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0if (p[55] !=3D 0x04) return false;
=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0bre=
ak;
=C2=A0=C2=A0=C2=A0=C2=A0case 1:
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[47] =
!=3D 0x01) return false;
=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[56] !=3D 0x03) return false;=
span>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0break;
=C2=A0=C2=A0=C2=A0=C2=A0case 2:=
p>
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0if (p[48] !=3D 0x01) return false;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[57] !=3D 0x02) =
return false;
=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
=C2=A0=C2=A0=C2=A0=C2=A0c=
ase 3:
=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0if (p[49] !=3D 0x01) return false;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[=
58] !=3D 0x01) return false;
=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
=C2=A0=C2=
=A0=C2=A0=C2=A0case 4:
=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0if (p[50] !=3D 0x01) return false;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0if (p[59] !=3D 0x00) return false;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0break;
=
=C2=A0=C2=A0=C2=A0=C2=A0default:
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
=C2=A0=C2=A0=C2=A0=C2=A0}
=
=C2=A0=C2=A0=C2=A0=C2=A0const size_t value_pos =3D 47 + x;
=
=C2=A0=C2=A0=C2=A0=C2=A0const uint64_t raw_value =
=3D ReadLE64(p + value_pos);
=C2=A0=C2=A0=C2=A0=C2=A0if (ra=
w_value > static_cast<uint64_t>(std::numeric_limits<int64_t>=
::max())) {
=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0return false;
=C2=A0=C2=A0=C2=A0=C2=A0}
=C2=A0=C2=A0=C2=A0=C2=A0con=
st int64_t nValue =3D static_cast<int64_t>(raw_value);
<=
b style=3D"font-weight: normal;">
=C2=A0=C2=A0=C2=A0=C2=A0if (!MoneyRange(nValue)) {
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return fa=
lse;
=C2=A0=C2=A0=C2=A0=C2=A0}
=C2=A0=C2=A0=C2=A0=C2=A0return true;
}
<=
/p>
static bool IsForbiddenMerkleInternalNode(=
=C2=A0=C2=A0=C2=A0=C2=A0const uint=
256& left,
=C2=A0=C2=A0=C2=A0=
=C2=A0const uint256& right)
{=
span>
=C2=A0=C2=A0=C2=A0=C2=A0unsigned cha=
r p[64];
=C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p, left.begin(=
), 32);
=C2=A0=C2=A0=C2=A0=C2=A0std=
::memcpy(p + 32, right.begin(), 32);
=C2=A0=C2=A0=C2=A0=C2=
=A0return IsForbiddenMerkleInternalNodePreimage64(p);
}
<=
/b>
A Merkle parent computation then che=
cks the preimage before hashing:
st=
atic uint256 ComputeMerkleParentChecked(
=C2=A0=C2=A0=C2=A0=C2=A0const uint256& left,
=C2=A0=C2=A0=C2=A0=C2=A0const uint256& right,
=C2=A0=C2=A0=C2=A0=C2=A0bool& invali=
d)
{
=C2=A0=C2=A0=C2=A0=C2=A0if (IsForbiddenMerkleInternalNode(left, r=
ight)) {
=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0invalid =3D true;
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0return uint256{};
=C2=A0=C2=A0=C2=A0=C2=A0}
<=
p>
=C2=A0=C2=A0=C2=A0=C2=A0unsigned char p[64];
=C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p, left.begin(), 32);
=C2=A0=C2=A0=C2=A0=C2=A0std::memcpy(p=
+ 32, right.begin(), 32);
<=
br />
=C2=A0=C2=A0=C2=A0=C2=A0return H=
ash(Span<const unsigned char>(p, 64));
}
This is the intended minimal rule. It checks =
the five possible 64-byte one-input, one-output transaction layouts directl=
y.
Miner considerations
Accidental violations by honest miners are expected to be rare=
.
Adversarial violations are poss=
ible. An attacker may grind transaction identifiers so that two transaction=
s, if placed as siblings in the transaction Merkle tree, form:
txid_A || txid_B
which encode=
s a forbidden minimal 64-byte transaction.
An attacker may attempt to influence sibling placement by fee =
rate, package construction, direct miner submission, or transaction orderin=
g effects.
Therefore miners MUST =
check candidate block templates before mining. Miners MUST NOT rely on acci=
dental violation probability.
Merkle construction failure recovery
If a candidate block tem=
plate violates this rule, the miner usually does not need to discard the en=
tire template. The violation is local to one or more internal Merkle node p=
reimages:
left_child_hash || right_=
child_hash
A miner can usually repair the candidate block=
by changing transaction order so that the offending pair of child hashes n=
o longer appears as siblings at the violating Merkle tree level.
=
Reco=
mmended recovery procedure
When Merkle root construction fails because an internal node preimage =
is forbidden, mining software SHOULD use the following procedure:
Record each offending internal node p=
reimage.
Identify the transa=
ction subtree contributing to each offending child hash.
Attempt to repair the block by shuffling trans=
action order while preserving consensus transaction-order constraints.
Recompute the Merkle root and re=
-run the internal-node preimage check.
If the shuffled template passes, mine the repaired template.=
span>
If shuffling fails repeatedl=
y, remove one or more transactions contributing to the offending subtree an=
d rebuild the template.
Preserving transaction-order constr=
aints
A shuffle MUST N=
OT violate transaction dependency ordering.
If transaction B spends an output created by transacti=
on A in the same block, then A MUST appear before <=
span style=3D"font-size: 11pt; font-family: "Roboto Mono", monosp=
ace; color: rgb(24, 128, 56); background-color: transparent; font-weight: 4=
00; font-style: normal; font-variant: normal; text-decoration: none; vertic=
al-align: baseline; white-space: pre-wrap;">B.
The coinbase transaction MUST =
remain the first transaction in the block.
Mining software SHOULD shuffle only transactions whose relativ=
e order is not constrained by in-block dependencies, or use a randomized to=
pological ordering of the block's transaction dependency graph.
<=
p>Simpl=
e shuffle algorithmA =
simple repair algorithm is:
1. Keep=
the coinbase fixed at index 0.
2. =
Build a dependency graph for all non-coinbase transactions.
3. Generate a randomized topological ordering of t=
he graph.
4. Construct the Merkle t=
ree using that ordering.
5. Reject =
the ordering if any internal node preimage is forbidden.
=
6. Retry with a new randomized topological ordering=
.
This changes Merkle sibling relationships without viola=
ting in-block transaction dependencies.
Repeated failure
If randomized repair fails repeate=
dly, mining software SHOULD remove transactions contributing to the repeate=
d offending subtree.
A reasonable=
policy is:
If Merkle construction =
fails after 2 independent shuffle attempts,
remove at least one transaction from each repeatedly offending pa=
ir or subtree.
For a bottom-level violation, the offendin=
g subtree usually corresponds to two sibling transaction identifiers:
txid_A || txid_B
In =
that case, the miner may remove either tx_A or tx_B.
For a higher-level violation, each ch=
ild hash commits to a subtree containing multiple transactions. In that cas=
e, the miner may:
1. Try another de=
pendency-preserving shuffle.
2. If =
the same higher-level violation recurs, remove one transaction from one chi=
ld subtree.
3. Prefer removing the =
lowest-feerate removable transaction that does not force removal of higher-=
feerate descendants.
<=
/b>
This policy does not need to identif=
y a malicious transaction. It only needs to produce a valid block template =
with minimal fee loss.
Fee impact
The expected fee impact for honest block templates should=
be negligible because accidental violations are rare.
If an adversary intentionally creates transactions th=
at cause violations when paired, shuffling will usually defeat the attempt =
without fee loss. If shuffling does not repair the template, removing one o=
r more offending transactions bounds the miner's exposure.
The adversary's practical effect is limited to po=
tentially causing some transactions to be omitted from a candidate block te=
mplate. The rule prevents upgraded miners from mining invalid blocks, provi=
ded miners check the Merkle construction before mining.
Relation to u=
nupgraded miners
Becau=
se accidental violations are rare, unupgraded miners are unlikely to encoun=
ter the rule during ordinary operation.
However, an adversary can construct transaction pairs intended to tr=
igger the rule under specific sibling placement.
Unupgraded miners that do not enforce this rule may mine a =
block that upgraded nodes reject after activation. Low accidental probabili=
ty improves deployment safety but is not a substitute for miner enforcement=
.
Probability analysis
This section estimates accidental violation probability under simpli=
fied randomness assumptions.
Random left || right
Assume the 64-byte internal node preimage is uniformly random.=
For the preimage to encode a min=
imal one-input, one-output 64-byte transaction, it must satisfy:
=
vin_count =3D 0x01
scriptSig_len =3D x, where x =E2=88=88 {0,1,2,3,4}
=
vout_count =3D 0x01 at the position determine=
d by x
scriptPubKey_len =3D 4 - x=
span>
nValue =E2=88=88 [0, MAX_MONEY]
Ignoring =
nValue, the structural probability is approx=
imately:
5 / 256^3
because there are five valid (scriptSig_len, scriptPubKey_len) sp=
lits, and three one-byte constraints:
vin_count
vout_count
<=
p dir=3D"ltr" style=3D"line-height: 1.38; margin-top: 0pt; margin-bottom: 0=
pt;">scriptPubKey_len
Numerically=
:
5 / 256^3 =E2=89=88 2.98023223876=
9531e-7
or approximately:
1 in 3,355,443
Including the output value money=
range:
MAX_MONEY =3D 21,000,000 * =
100,000,000
=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
=
For a uniformly random unsigned 64-bit output value, the probabilit=
y of being in range is approximately:
(MAX_MONEY + 1) / 2^64
=E2=89=88=
1.1384122811097797e-4
Therefore the approximate probabil=
ity that a random 64-byte preimage is structurally valid and has an in-rang=
e output value is:
(5 / 256^3) * ((=
MAX_MONEY + 1) / 2^64)
=E2=89=88 3.=
392733219831406e-11
=
b>
or approximately:
1 in 29,475,000,000
Random left || left
For an odd-entry duplicated Merkle node, the pr=
eimage has the form:
left || left=
span>
where the first 32 bytes equal the last 32 bytes.=
p>
Let the 32-byte half be:
=
A[0..31]
Then:
=
P[0..31]=C2=A0 =3D A[0..31]
P[32..63] =3D A[0..31]
For the same one-=
input, one-output 64-byte transaction shape:
P[4]=C2=A0 =3D 0x01
P[41]=
=3D scriptSig_len =3D x
P[vout_cou=
nt_pos] =3D 0x01
P[scriptpubkey_len=
_pos] =3D 4 - x
=
p>
Because positions after byte 31 alias pos=
itions in the first half:
P[i] =3D =
A[i mod 32]
The relevant positions are:
=
vin_count_pos=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D 4
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
vout_count_pos =C2=A0 =C2=A0 =C2=A0 =3D 46 + x=C2=A0 =E2=89=
=A1 14 + x mod 32
scriptpubkey_len_=
pos =3D 55 + x=C2=A0 =E2=89=A1 23 + x mod 32
The constrai=
nts are:
A[4]=C2=A0 =C2=A0 =C2=A0 =
=3D 0x01
A[9]=C2=A0 =C2=A0 =C2=A0 =
=3D x
A[14 + x] =3D 0x01
=
A[23 + x] =3D 4 - x
For eac=
h fixed x, these are four independent one-byte constraints under th=
e random-half model.
Thus the str=
uctural probability is approximately:
5 / 256^4
=E2=89=88 1.1641532182=
693481e-9
or approximately:
1 in 858,993,459
The output value begins at:=
value_pos =3D 47 + x
=
which aliases to an 8-byte window in the random 32-byte half:=
p>
A[15 + x .. 22 + x]
Usi=
ng the same simplified independence approximation, the probability of being=
in MoneyRange is approximately:
(MAX_MONEY + 1) / 2^64
=E2=89=
=88 1.1384122811097797e-4
So the approximate probability =
that a random left || l=
eft preimage is structurally valid and has a=
n in-range output value is:
(5 / 25=
6^4) * ((MAX_MONEY + 1) / 2^64)
=E2=
=89=88 1.3252864140005492e-13
or approximately:
1 in 7,545,600,000,000
Block-lev=
el accidental probability
A block with n transactions has approximately n - 1 int=
ernal Merkle nodes, plus duplicated-node cases depending on tree shape.
Using the rough random left || right estimate:
p =E2=89=88 3.39=
e-11
<=
span style=3D"font-size: 11pt; font-family: Arial, sans-serif; color: rgb(0=
, 0, 0); background-color: transparent; font-weight: 400; font-style: norma=
l; font-variant: normal; text-decoration: none; vertical-align: baseline; w=
hite-space: pre-wrap;">A block with 10,000 transactions has approximate acc=
idental violation probability:
1 - =
(1 - p)^9999 =E2=89=88 3.39e-7
or roughly:
1 in 2,950,000 blocks
This is a=
simplified estimate. Actual txids are not perfect independent random sampl=
es in all cases, duplicated nodes have lower estimated probability, and add=
itional implementation details may reduce or alter the rate.
The deployment-relevant conclusion is:=
p>
Honest accidental violations should be rare=
.
Adversarial violations are possib=
le.
Miners must enforce the rule.=
span>
Backward compatibility
This is a soft fork. Blocks violating the new rule were previ=
ously valid and become invalid after activation.
Unupgraded full nodes may accept violating blocks after act=
ivation. Activation therefore requires ordinary soft-fork deployment proced=
ures.
Unupgraded SPV clients rema=
in vulnerable to the legacy proof ambiguity. SPV clients must update their =
Merkle proof validation logic to obtain the benefit of this rule.
Tes=
t vectors
Test vectors=
should include:
A block who=
se transaction Merkle internal node preimages do not encode minimal 64-byte=
transactions. The block is valid.
A block containing a 64-byte transaction whose serialization does no=
t appear as an internal node preimage. The block is valid.
<=
li dir=3D"ltr" style=3D"list-style-type: decimal; font-size: 11pt; font-fam=
ily: Arial, sans-serif; color: rgb(0, 0, 0); background-color: transparent;=
font-weight: 400; font-style: normal; font-variant: normal; text-decoratio=
n: none; vertical-align: baseline; white-space: pre;">A block where an internal node preimage enc=
odes a minimal 64-byte transaction. The block is invalid.
A block where an odd-entry duplicated preim=
age h || h encodes a minimal 64-byte transaction. The block is inva=
lid.
- =
An SPV proof where one =
branch preimage encodes a minimal 64-byte transaction. The proof is rejecte=
d.
An SPV proof for a 64-by=
te transaction where no branch preimage encodes a minimal 64-byte transacti=
on. The proof is accepted if otherwise valid.
Open question=
s
Should the rule=
include only the explicit minimal 64-byte legacy transaction shape above, =
or should it call the full consensus transaction deserializer?
=
li>Should future transaction serialization =
changes be required to preserve this exact forbidden-preimage invariant?
Should pre-activation relay p=
olicy discourage transaction pairs that can form forbidden sibling preimage=
s?
Should mining software st=
andardize a recovery procedure for failed Merkle construction, or should th=
is remain implementation-specific?
Should SPV proof formats include an explicit version bit indicating=
branch-preimage checking support?
--
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/f97afcc5-54ba-4284-8e9b-e8c35c7101f6n%40googlegroups.com.
------=_Part_487477_1559598651.1780336000367--
------=_Part_487476_1918830888.1780336000367--