Skip to content

Commit f3bc1a7

Browse files
committed
Merge bitcoin#26265: POLICY: Relax MIN_STANDARD_TX_NONWITNESS_SIZE to 65 non-witness bytes
b2aa9e8 Add release note for MIN_STANDARD_TX_NONWITNESS_SIZE relaxation (Greg Sanders) 8c5b364 Relax MIN_STANDARD_TX_NONWITNESS_SIZE to 65 non-witness bytes (Greg Sanders) Pull request description: Since the original fix was set to be a "reasonable" transaction to reduce allocations and the true motivation later revealed, it makes sense to relax this check to something more principled. There are more exotic transaction patterns that could take advantage of a relaxed requirement, such as 1 input, 1 output OP_RETURN to burn a utxo to fees for CPFP purposes when change isn't practical. Two changes could be accomplished: 1) Anything not 64 bytes could be allowed 2) Anything above 64 bytes could be allowed In the Great Consensus Cleanup, suggestion (2) was proposed as a consensus change, and is the simpler of the two suggestions. It would not allow an "empty" OP_RETURN but would reduce the required padding from 22 bytes to 5. The functional test is also modified to test the actual case we care about: 64 bytes Related mailing list discussions here: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2022-October/020995.html And a couple years earlier: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2020-May/017883.html ACKs for top commit: achow101: reACK b2aa9e8 glozow: reACK b2aa9e8 pablomartin4btc: re-ACK bitcoin@b2aa9e8 jonatack: ACK b2aa9e8 with some suggestions Tree-SHA512: c1ec1af9ddcf31b2272209a4f1ee0c5607399f8172e5a1dfd4604cf98bfb933810dd9369a5917ad122add003327c9fcf6ee26995de3aca41d5c42dba527991ad
2 parents 6d40a1a + b2aa9e8 commit f3bc1a7

File tree

6 files changed

+63
-19
lines changed

6 files changed

+63
-19
lines changed

doc/release-notes-26265.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
P2P and network changes
2+
---------
3+
4+
- Transactions of non-witness size 65 and above are now allowed by mempool
5+
and relay policy. This is to better reflect the actual afforded protections
6+
against CVE-2017-12842 and open up additional use-cases of smaller transaction sizes. (#26265)

src/policy/policy.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ static constexpr unsigned int DEFAULT_BLOCK_MAX_WEIGHT{MAX_BLOCK_WEIGHT - 4000};
2525
static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1000};
2626
/** The maximum weight for transactions we're willing to relay/mine */
2727
static constexpr unsigned int MAX_STANDARD_TX_WEIGHT{400000};
28-
/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */
29-
static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{82};
28+
/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64 */
29+
static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{65};
3030
/** Maximum number of signature check operations in an IsStandard() P2SH script */
3131
static constexpr unsigned int MAX_P2SH_SIGOPS{15};
3232
/** The maximum number of sigops we're willing to relay/mine in a single tx */

src/validation.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -690,10 +690,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
690690
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
691691
}
692692

693-
// Do not work on transactions that are too small.
694-
// A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes.
695-
// Transactions smaller than this are not relayed to mitigate CVE-2017-12842 by not relaying
696-
// 64-byte transactions.
693+
// Transactions smaller than 65 non-witness bytes are not relayed to mitigate CVE-2017-12842.
697694
if (::GetSerializeSize(tx, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE)
698695
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "tx-size-small");
699696

test/functional/data/invalid_txs.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,19 @@
4646
OP_MOD,
4747
OP_MUL,
4848
OP_OR,
49+
OP_RETURN,
4950
OP_RIGHT,
5051
OP_RSHIFT,
5152
OP_SUBSTR,
52-
OP_TRUE,
5353
OP_XOR,
5454
)
5555
from test_framework.script_util import (
56+
MIN_PADDING,
57+
MIN_STANDARD_TX_NONWITNESS_SIZE,
5658
script_to_p2sh_script,
5759
)
5860
basic_p2sh = script_to_p2sh_script(CScript([OP_0]))
5961

60-
6162
class BadTxTemplate:
6263
"""Allows simple construction of a certain kind of invalid tx. Base class to be subclassed."""
6364
__metaclass__ = abc.ABCMeta
@@ -122,7 +123,9 @@ class SizeTooSmall(BadTxTemplate):
122123
def get_tx(self):
123124
tx = CTransaction()
124125
tx.vin.append(self.valid_txin)
125-
tx.vout.append(CTxOut(0, CScript([OP_TRUE])))
126+
tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
127+
assert len(tx.serialize_without_witness()) == 64
128+
assert MIN_STANDARD_TX_NONWITNESS_SIZE - 1 == 64
126129
tx.calc_sha256()
127130
return tx
128131

test/functional/mempool_accept.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
MAX_BIP125_RBF_SEQUENCE,
1515
COIN,
1616
COutPoint,
17+
CTransaction,
1718
CTxIn,
19+
CTxInWitness,
1820
CTxOut,
1921
MAX_BLOCK_WEIGHT,
2022
MAX_MONEY,
@@ -26,13 +28,19 @@
2628
OP_0,
2729
OP_HASH160,
2830
OP_RETURN,
31+
OP_TRUE,
2932
)
3033
from test_framework.script_util import (
34+
DUMMY_MIN_OP_RETURN_SCRIPT,
3135
keys_to_multisig_script,
36+
MIN_PADDING,
37+
MIN_STANDARD_TX_NONWITNESS_SIZE,
3238
script_to_p2sh_script,
39+
script_to_p2wsh_script,
3340
)
3441
from test_framework.util import (
3542
assert_equal,
43+
assert_greater_than,
3644
assert_raises_rpc_error,
3745
)
3846
from test_framework.wallet import MiniWallet
@@ -333,6 +341,35 @@ def run_test(self):
333341
maxfeerate=0,
334342
)
335343

344+
# Prep for tiny-tx tests with wsh(OP_TRUE) output
345+
seed_tx = self.wallet.send_to(from_node=node, scriptPubKey=script_to_p2wsh_script(CScript([OP_TRUE])), amount=COIN)
346+
self.generate(node, 1)
347+
348+
self.log.info('A tiny transaction(in non-witness bytes) that is disallowed')
349+
tx = CTransaction()
350+
tx.vin.append(CTxIn(COutPoint(int(seed_tx[0], 16), seed_tx[1]), b"", SEQUENCE_FINAL))
351+
tx.wit.vtxinwit = [CTxInWitness()]
352+
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
353+
tx.vout.append(CTxOut(0, CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 2)))))
354+
# Note it's only non-witness size that matters!
355+
assert_equal(len(tx.serialize_without_witness()), 64)
356+
assert_equal(MIN_STANDARD_TX_NONWITNESS_SIZE - 1, 64)
357+
assert_greater_than(len(tx.serialize()), 64)
358+
359+
self.check_mempool_result(
360+
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'tx-size-small'}],
361+
rawtxs=[tx.serialize().hex()],
362+
maxfeerate=0,
363+
)
364+
365+
self.log.info('Minimally-small transaction(in non-witness bytes) that is allowed')
366+
tx.vout[0] = CTxOut(COIN - 1000, DUMMY_MIN_OP_RETURN_SCRIPT)
367+
assert_equal(len(tx.serialize_without_witness()), MIN_STANDARD_TX_NONWITNESS_SIZE)
368+
self.check_mempool_result(
369+
result_expected=[{'txid': tx.rehash(), 'allowed': True, 'vsize': tx.get_vsize(), 'fees': { 'base': Decimal('0.00001000')}}],
370+
rawtxs=[tx.serialize().hex()],
371+
maxfeerate=0,
372+
)
336373

337374
if __name__ == '__main__':
338375
MempoolAcceptanceTest().main()

test/functional/test_framework/script_util.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@
1313
OP_EQUAL,
1414
OP_EQUALVERIFY,
1515
OP_HASH160,
16+
OP_RETURN,
1617
hash160,
1718
sha256,
1819
)
1920

2021
# To prevent a "tx-size-small" policy rule error, a transaction has to have a
21-
# non-witness size of at least 82 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
22+
# non-witness size of at least 65 bytes (MIN_STANDARD_TX_NONWITNESS_SIZE in
2223
# src/policy/policy.h). Considering a Tx with the smallest possible single
2324
# input (blank, empty scriptSig), and with an output omitting the scriptPubKey,
2425
# we get to a minimum size of 60 bytes:
@@ -28,15 +29,15 @@
2829
# Output: 8 [Amount] + 1 [scriptPubKeyLen] = 9 bytes
2930
#
3031
# Hence, the scriptPubKey of the single output has to have a size of at
31-
# least 22 bytes, which corresponds to the size of a P2WPKH scriptPubKey.
32-
# The following script constant consists of a single push of 21 bytes of 'a':
33-
# <PUSH_21> <21-bytes of 'a'>
34-
# resulting in a 22-byte size. It should be used whenever (small) fake
35-
# scriptPubKeys are needed, to guarantee that the minimum transaction size is
36-
# met.
37-
DUMMY_P2WPKH_SCRIPT = CScript([b'a' * 21])
38-
DUMMY_2_P2WPKH_SCRIPT = CScript([b'b' * 21])
39-
32+
# least 5 bytes.
33+
MIN_STANDARD_TX_NONWITNESS_SIZE = 65
34+
MIN_PADDING = MIN_STANDARD_TX_NONWITNESS_SIZE - 10 - 41 - 9
35+
assert MIN_PADDING == 5
36+
37+
# This script cannot be spent, allowing dust output values under
38+
# standardness checks
39+
DUMMY_MIN_OP_RETURN_SCRIPT = CScript([OP_RETURN] + ([OP_0] * (MIN_PADDING - 1)))
40+
assert len(DUMMY_MIN_OP_RETURN_SCRIPT) == MIN_PADDING
4041

4142
def key_to_p2pk_script(key):
4243
key = check_key(key)

0 commit comments

Comments
 (0)