Relax MIN_STANDARD_TX_NONWITNESS_SIZE to 65 non-witness bytes (tx fees and policy, mempool)

https://github.com/bitcoin/bitcoin/pull/26265

Host: glozow  -  PR author: instagibbs

The PR branch HEAD was e5adc1a284a9292362e75501adc9f71ac6ecdf6e at the time of this review club meeting.

Notes

  • Mempool policy requires that transactions be at least 82 bytes (non-witness size). This rule was introduced in PR #11423. The original justification given was “A transaction with 1 segwit input and 1 P2WPKH output has non-witness size of 82 bytes. Transactions smaller than this are not relayed to reduce unnecessary malloc overhead.”

  • The true motivation was later documented as CVE-2017-12842 and PR #16885; a 64-byte transaction could be used to trick SPV clients.

  • While 64-byte transactions are nonstandard in Bitcoin Core, it is still possible to create one that is consensus-valid. It has been proposed in PR #15482 to disallow transactions smaller than 65 bytes in consensus. This proposal has not been accepted.

  • PR #26265 relaxes the policy rule from 82-byte minimum to 65-byte minimum. Another approach could be to simply disallow 64-byte transactions.

  • The author also posted this proposal to the mailing list, adding that a 65-byte minimum would allow transactions with 1 input, 1 OP_RETURN output to be standard. Optech newsletters #99 and #222 have discussed this topic.

Questions

  1. Did you review the PR? Concept ACK, approach ACK, tested ACK, or NACK?

  2. Why was the minimum transaction size 82 bytes? Can you describe the attack? Why does setting this policy help prevent the attack? Does it eliminate the attack vector entirely?

  3. What does “non-witness size” mean, and why do we care about the “non-witness” distinction?

  4. Why might we want to change the minimum transaction size to 65 bytes, apart from the fact that we no longer need to try to obfuscate the CVE outlined in the notes?

  5. Between disallowing sizes less than 65 bytes and sizes equal to 64 bytes, which approach do you think is better and why? What are the different implications of both approaches?

  6. What is “OP_RETURN padding”? What do 10, 9, and 41 represent in the calculation of MIN_PADDING?

  7. What does the “tiny transaction” created here consist of?

  8. What is the tiny transaction’s size serialized with witness (Hint: see CTransaction functions)?

Meeting Log

  117:00 <glozow> #startmeeting
  217:00 <glozow> hi
  317:00 <LarryRuane> hi!
  417:00 <instagibbs> hi
  517:00 <stickies-v> hi
  617:00 <glozow> hi instagibbs! thanks for coming
  717:00 <b_101> hi!
  817:00 <inauman> hello everyone
  917:00 <lightlike> hi
 1017:01 <brunoerg> hi
 1117:01 <instagibbs> my pleasure
 1217:01 <hernanmarino> Hi !
 1317:01 <glozow> and hello to everyone else of course! Welcome to bitcoin core pr review club, we're looking at #26265 today: https://bitcoincore.reviews/26265
 1417:01 <pablomartin> hello!
 1517:01 <glozow> have y'all had a chance to review the PR and/or look at the notes?
 1617:02 <LarryRuane> some .. 0.45y
 1717:02 <hernanmarino> yes
 1817:02 <instagibbs> maybe
 1917:02 <lightlike> yes
 2017:03 <stickies-v> y for notes - but I mostly spent time reading about the vulnerability instead of the code :(
 2117:03 <LarryRuane> I rabbit-holed a bit looking at how merkle blocks work
 2217:03 <glozow> LarryRuane: hernanmarino: lightlike: stickies-v: excellent, would any of you like to summarize what this PR is doing?
 2317:03 <b_101> partially
 2417:04 <LarryRuane> I think it's loosening a standardness rule, so that smaller transactions would now be considered standard, all the way down to 65 bytes
 2517:04 <hernanmarino> It reduces the standard transaction size to 65 bytes, from previous value of 82
 2617:05 <LarryRuane> that's a question I had on the notes, actually .. the nodes mention the current min tx size is 85, but is it 82?
 2717:05 <LarryRuane> *notes
 2817:05 <hernanmarino> i believe it s a typo
 2917:05 <LarryRuane> (i'm so used to typing the word "nodes" haha)
 3017:05 <glozow> oh oops, where did I get 85 from 🤔 my bad
 3117:05 <hernanmarino> :)
 3217:05 <stickies-v> interesting that the only non-test code change is changing an int from 82 to 65
 3317:06 <glozow> stickies-v: yes, sometimes the codebase is quite clean eh?
 3417:07 <b_101> stickies: +1
 3517:07 <glozow> Can someone tell us why the minimum used to be 82?
 3617:07 <LarryRuane> stickies-v: yes https://github.com/bitcoin/bitcoin/pull/26265/files#diff-1fc0f6b5081e8ed5dfa8bf230744ad08cc6f4c1147e98552f1f424b0492fe9bdL28
 3717:08 <hernanmarino> Because it mitigated an attack regarding SPV wallets. Also it was believed to be the minimum size of a valid transaction , i.e. transactions with 1 input and 1 output.
 3817:08 <inauman> I think min 82 was because one segwit input and one p2wpkh was around that size
 3917:08 <LarryRuane> I think the answer is given in the comment (that's being removed): "(1 segwit input + 1 P2WPKH output = 82 bytes)"
 4017:08 <LarryRuane> (but i didn't confirm that with murch :) )
 4117:09 <stickies-v> and that was the smallest standard transaction possible at the time
 4217:09 <glozow> hernanmarino has given the real reason
 4317:10 <LarryRuane> so was it not reduced to 65 at that time because doing so would create a clue about the existence of the vulnerability?
 4417:10 <glozow> I don't think anyone believed that was the minimum size of a valid transaction, but yes it would effectively be the smallest standard payment, so this rule was disguised as a little exit early condition
 4517:10 <LarryRuane> hernanmarino: "believed to be ..." -- but was it actually the min size?
 4617:11 <glozow> LarryRuane: I believe so yes. hidden bug fix. was also grouped with a few other standardness rules.
 4717:11 <hernanmarino> LarryRuane: Well, there are now valid transactions smaller than that, but they are not valid spends
 4817:12 <Murch> 82 might stem from the size of a stripped native segwit transaction
 4917:12 <LarryRuane> was part of the disguise also that this was part of a pretty large PR? (17 files changed)
 5017:12 <hernanmarino> i.e OP_RETURNs and other valid use cases
 5117:12 <glozow> hernanmarino: hm, I think you might be using the word "valid" with 2 different meanings
 5217:12 <Murch> The stripped size of a P2WPKH input is 41 B, 31 B for the output, and 10 B for the header = 82 B
 5317:13 <hernanmarino> glozow: yes i was trying to type fast :)
 5417:13 <Murch> With an OP_RETURN output, I think we could get 41+10+10 = 61 B though
 5517:13 <stickies-v> OP_RETURN was made standard in 0.9.0 it seems (released in March 2014), so it seems that smaller standard txs were indeed possible prior to #11423 (May 2018) ?
 5617:14 <LarryRuane> Murch: what does "stripped size" mean? no witness?
 5717:14 <instagibbs> stickies-v, ah good historical digging
 5817:14 <stickies-v> https://bitcoin.org/en/release/v0.9.0
 5917:14 <Murch> yeah
 6017:14 <glozow> stickies-v: good dig. yes, hence standard *payment* heh
 6117:15 <Murch> Oh, that's significantly before segwit, never mind.
 6217:15 <glozow> What does “non-witness size” mean, and why do we care about the “non-witness” distinction?
 6317:15 <LarryRuane> I always like to recommend this @murch tweet, good one to bookmark ... but it shows non-stripped size, right? maybe have a stripped version? https://twitter.com/murchandamus/status/1262062602298916865
 6417:16 <hernanmarino> glozow: It means the size of transactions not including witnesses / signatures. But I'm not sure about your second question
 6517:16 <LarryRuane> glozow: is it because BIP31 (bloom filter) is always only txid, not wtxid?
 6617:16 <lightlike> given that the attack was costly ($1M) according to https://github.com/advisories/GHSA-v55p-4chq-6grj and that it seems unlikely that people would use SPV clients for payments that are that large, was it really necessary to fix this covertly?
 6717:17 <LarryRuane> sorry I think that's the wrong bip number
 6817:17 <LarryRuane> bip 37
 6917:18 <glozow> lightlike: idk!
 7017:18 <stickies-v> we care about the non-witness distinction because (as part of the segwit upgrade) witness data is excluded from calculation of the merkle root
 7117:18 — Murch uploaded an image: (83KiB) < https://libera.ems.host/_matrix/media/v3/download/matrix.org/zpgJxjvPsJXlQoqtIrMevKTN/image.png >
 7217:18 <instagibbs> lightlike, what would happen if your intuition was wrong?
 7317:18 <LarryRuane> lightlike: good q, I was wondering that exact thing too
 7417:19 <LarryRuane> Murch: thank you!
 7517:19 <instagibbs> lightlike, for some historical fun, what if someone was using the RPC for verification of merkle proofs: https://github.com/bitcoin/bitcoin/pull/13452
 7617:19 <stickies-v> and since the attack requires the malicious transaction to be 64 bytes in the merkle root construction (so it looks like inner nodes), we need to exclude witness data from it
 7717:20 <hernanmarino> Murch: cool ! Thanks
 7817:20 <LarryRuane> stickies-v: right because the merkle block stuff is based on txid (stripped tx), not wtxid
 7917:21 <glozow> stickies-v: ye I think that's right!
 8017:21 <LarryRuane> I'm wondering, is BIP 37 still used today? I know most node configurations don't enable bloom filters. Can SPV clients use the newer block filters?
 8117:22 <glozow> er, I don't think we're talking about bloom filters here. just merkle proofs. SPV clients might use both, but they're different things
 8217:22 <hernanmarino> stickies-v: LarryRuane: thanks, that seems like the reason
 8317:22 <LarryRuane> oh ok, i mistakenly thought merkle proofs were only used in conjunction with bloom filters (bip37)
 8417:23 <glozow> Why does setting this policy help prevent the attack? Does it eliminate the attack vector entirely?
 8517:25 <LarryRuane> because inner merkle tree nodes can only be exactly 64 bytes? so requiring tx size to never be 64 prevents a tx from being interpreted as an inner node? I think it does eliminate the attack entirely (but not sure)
 8617:25 <stickies-v> LarryRuane: good point, I think merkle proofs are not inherent to bloom filters but I don't know where Bitcoin Core would provide merkle proofs elsewhere, since indeed I think they're not used with compact block filters
 8717:26 <glozow> LarryRuane: correct for the first part. It does not eliminate the attack entirely.
 8817:26 <LarryRuane> stickies-v: yes if you search the code base for `CMerkleBlock`, you'll see it's only used with `IsMsgFilteredBlk`
 8917:26 <inauman> Because the nodes in merkle tree are used in pairs (32+32) and a transactions can also be of 64 bytes, an attacker can masquerade a node with a transaction..by increasing it to 65, this attack can be eliminated
 9017:27 <hernanmarino> It mitigates this risk beacuse the attack requires exactly 64 byte transactions. Regarding the last question I understand it eliminates the attack completely, but I have a hunch telling me this is not the right answer : )
 9117:27 <andrewtoth_> it prevents the attacking tx from being relayed to miners, but it doesn't prevent it from being mined so it can still be included in a block
 9217:27 <stickies-v> this is only about standardness/relay right, so a miner could still pull this attack off
 9317:27 <stickies-v> or direct submission to miners, but yeah like andrewtoth_ said
 9417:27 <glozow> andrewtoth_: stickies-v: bingo
 9517:27 <lightlike> it's just policy, so if you manage to get the tx to a favourable miners somehow, you can still be successful
 9617:27 <hernanmarino> stickies-v: they can, I think
 9717:28 <LarryRuane> stickies-v: andrewtoth_: +1
 9817:28 <glozow> yes, the attack is possible if it's mined in a block. relaying through Bitcoin Core nodes is not the only way to achieve that.
 9917:28 <hernanmarino> glozow: Ohh, it was so obvious ! :)
10017:28 <glozow> Why might we want to change the minimum transaction size to 65 bytes, apart from the fact that it's unnecessary to try to obfuscate the CVE outlined in the notes?
10117:28 <amovfx_> hi
10217:29 <LarryRuane> glozow: I'm not sure, but is it because there are legit transactions with sizes between 65 and 82?
10317:29 <hernanmarino> Because there are use cases of transactions of this size, and lower than 82
10417:30 <andrewtoth_> I believe this is part of a proposal to get OP_TRUE payments standardized, which would allow anyone to CPFP the tx. But first the size of such an output must be allowed to be relayed?
10517:30 <andrewtoth_> *size of such a tx including that output
10617:31 <lightlike> according to Peter Todd on the ML, one legit use case is to burn dust in order to reduce the UTXO set. Is there another one?
10717:31 <LarryRuane> higher level question prompted by glozow's comment (about Bitcoin Core): for something like this, would we expect the maintainers of other bitcoin clients to make the analogous changes? I would imagine they follow the ML?
10817:32 <glozow> andrewtoth_: yes, OP_TRUE for anyone to CPFP is a potential use
10917:32 <_aj_> if you're going to burn dust, you could supply an ANYONECANPAY|NONE signature, and then once you've got two dust outputs to combine, you're tx is already >72 bytes stripped just to reference the input utxos
11017:33 <_aj_> (>90 bytes counting version, locktime, counts, and output amount too, whatever)
11117:34 <LarryRuane> _aj_: just so I understand, you're saying that burning dust isn't a reason to allow < 82 byte transactions?
11217:35 <glozow> oh wait is OP_TRUE no longer reasoning? "I went down the rabbit hole with naked OP_TRUE, forgetting that wsh(OP_TRUE) is standard for making and spending... will remove"
11317:36 <_aj_> LarryRuane: if you've got a lot of dust to burn, then better to combine it all into a single tx (saving 10B tx overhead and 10B output overhead)
11417:36 <LarryRuane> _aj_: +1 thanks
11517:36 <andrewtoth_> glozow: interesting...link?
11617:37 <instagibbs> glozow, that was for test environment, not use
11717:37 <glozow> oh ok
11817:37 <glozow> nvm
11917:37 <instagibbs> naked op_true spends arent standard, I was trying to mine those outputs, ended up not helping make an actual test
12017:37 <instagibbs> I forgot that stripped size is all that matters while writing
12117:38 <_aj_> the only use i came up with that felt like it made sense was if you wanted to CPFP by burning an entire output to fees; which is plausible but seems kind of rare (better than forcing you to add another utxo as input just so you add a change output, and then not even be able to claim all your extra input back as change)
12217:40 <glozow> _aj_: seems plausible.
12317:40 <instagibbs> _aj_, I agree that;s the use case that drove this PR, after talking to tbast, where he wanted exactly this
12417:40 <glozow> Between disallowing sizes less than 65 bytes and sizes equal to 64 bytes, which approach do you think is better and why? What are the different implications of both approaches?
12517:41 <LarryRuane> at first I liked disallowing < 65, but I also seem to like Peter's recent comment https://github.com/bitcoin/bitcoin/pull/26265#issuecomment-1292303954
12617:42 <glozow> so one question we might ask is "is there a use case for a transaction that's 63 bytes?"
12717:44 <LarryRuane> or even if we can't think of one, maybe one will arise in the future?
12817:45 <theStack> not sure if this would count as "legit use case", but what about txs with 1 segwit input and 1 null data (OP_RETURN) output? didn't those have to have a minimum size of 20 bytes nulldata to pass the check?
12917:46 <amovfx_> There is one mentioned in the comments that a psbt can be 63B
13017:46 <glozow> theStack: yeah I think that's 63B
13117:47 <instagibbs> should be 62?
13217:47 — glozow waves hand
13317:47 <_aj_> instagibbs: an empty scriptPubKey/scriptSig gives 60B minimum, yes/no?
13417:48 <instagibbs> let me read my excellent tests /joke
13517:48 <instagibbs> yes, 60, sorry, so 61 is minimum unspendable(standard) output
13617:49 <instagibbs> https://github.com/bitcoin/bitcoin/pull/26398/files#diff-28d3b20588dcdd115f47a2c83254a9dcf413b6b6dbe5ad1833e922a042b0023eL24
13717:49 <glozow> alright let's break down the test shall we. what does "tiny tx" consist of? https://github.com/bitcoin-core-review-club/bitcoin/commit/e5adc1a284a9292362e75501adc9f71ac6ecdf6e#diff-a99d72c0ed66c256169e92327e04ab223e71f5ef598e14aac0ff44dc2a1194daR348-R352
13817:50 <Murch> stripped native segwit input is 41 B + 10 B header + 8 B value, 1 B SPK length + 1 B OP_TRUE/OP_RETURN = 61 B
13917:51 <Murch> _aj_: Can SPK be completely empty?
14017:51 <instagibbs> Murch, yes
14117:51 <Murch> I don't think that would evaluate to true?
14217:51 <instagibbs> scriptsig of OP_TRUE
14317:51 <_aj_> Murch: you'd supply "1" as scriptSig to spend
14417:52 <Murch> okay, then I think it would be 60 B?
14517:52 <glozow> ah wow. what would we call this output? P2MT?
14617:52 <instagibbs> its non-standard, I think we'd call this "bare script"
14717:52 <glozow> why isn't it standard?
14817:53 <instagibbs> if an output scriptpubkey doesn't match a known template, it's considered non-standard. It was never given one. very much into the rabbit hold now...
14917:53 <Murch> I asked the Pieterpedia, he agrees that SPK can be empty and 60 B is minimum tx size ^^
15017:54 <Murch> The Rabbithold sounds like the fortress of the Taproot proponents.
15117:54 <glozow> hahaha
15217:55 <hernanmarino> haha
15317:55 <sipa> Hi.
15417:57 <glozow> Next question. What is the tiny transaction’s size serialized with witness?
15517:58 <glozow> theStack: the minimum 20 bytes nulldata was in order to meet the previous minimum size of 82. so they're both decreased.
15617:58 <glozow> (assuming I interpreted your question correctly)
15717:59 <Murch> Since this transaction doesn't have any witness, the same size
15818:00 <glozow> witness 0 is a OP_TRUE?
15918:00 <glozow> Seems we have reached the end of the hour
16018:00 <glozow> #endmeeting
16118:01 <sipa> An empty output would be spent with a ␝scriptSig␏ of OP_TRUE.
16218:01 <Murch> Putting the OP_TRUE into the witness would be bigger than just putting it into the scriptSig
16318:01 <sipa> Not a witness. This isn't a segwit output.
16418:01 <andrewtoth_> thanks glozow!
16518:01 <Murch> Also you'd require a witness program in the output then
16618:01 <theStack> glozow: yeah, i was trying to find out whether users could now relay 1-segwit-input 1-nulldata-outputs with smaller nulldata payload than before (though i'm not sure why anyone would need that)
16718:01 <glozow> i think we might be talking about different things. i was talking about the tiny tx linked above, at https://github.com/bitcoin-core-review-club/bitcoin/commit/e5adc1a284a9292362e75501adc9f71ac6ecdf6e#diff-a99d72c0ed66c256169e92327e04ab223e71f5ef598e14aac0ff44dc2a1194daR348-R352>&gt;