What Grinding Has Actually Saved on Chain
A few months ago I wrote about whether you can fingerprint a hardware wallet from its signatures. One of the vectors was low-$r$ grinding: some wallets re-sign a transaction until $r$ fits in one byte less, which pins their signatures at 71 bytes instead of a coin flip between 71 and 72.
Back then I only cared whether a device grinds. This post asks the other half of it:
How many bytes has all that grinding actually saved?
One byte is a ridiculous unit to care about. And yet Bitcoin Core shipped a feature to save it and other wallets copied it, so either everyone is wasting their time or bytes add up.
Let’s count them ;)
Where the byte comes from
An ECDSA signature travels DER-encoded:
30 <len> 02 <len_r> <r> 02 <len_s> <s> <sighash>
DER integers are signed, two’s complement. If the top byte of $r$ or $s$ has its high bit set, the encoder prepends a 0x00 so the value isn’t read as negative. That padding byte is what this whole post is about.
A value that dodges it is low: $r < 2^{255}$, or $s \leq n/2$.1 Each low value saves at least one byte.2
Two tricks that are not the same
| how you get a low value | cost | |
|---|---|---|
| $s$ | negate it: if $(r, s)$ is valid, so is $(r, n - s)$ | free |
| $r$ | throw the nonce away and sign again | ~2 signatures |
$s$ is free because both halves authorize the same thing, so a signer just keeps the one it likes. Core started doing that in v0.9.0 (March 2014) as a malleability fix, and v0.10.3 and v0.11.1 made high-$s$ non-standard in October 2015. The bytes were a side effect.
$r$ comes from the nonce, $r = (kG)_x \bmod n$, so you cannot negate your way to a low one. A wallet that wants it has to sign again. Core shipped that in v0.17.0 (October 2018).
That difference is the whole story of the two curves below.
The data
mainnet.observer publishes a daily count of how many ECDSA signatures had a low or a high $r$, and the same for $s$. I read the backend first, to check that its thresholds are the ones that decide the padding byte.
Then I wrote a small Rust tool that pulls those series, caches them and does the accounting. It reports two numbers:
- saved — every low-$r$ and low-$s$ signature, one byte each.
- missed — every high-$r$ and high-$s$ signature: bytes that went on the chain and did not have to.
Half of the saved ones are luck, not grinding, since $r$ comes out low half the time on its own. The byte is saved either way, so it stays in the count and I split it out at the end.
Charts
Low-$s$ sits at the coin flip until 2014, goes almost vertical through 2015, and is pinned at 100% from 2016 on, once high-$s$ was non-standard.
Low-$r$ is flat at 50% for nine years, bends in October 2018 when v0.17.0 shipped, and peaks at 70.2% in December 2022. Then it sags, and has been drifting between 60% and 68% ever since.
I could not work out what drove the sag. My first guess was the input mix, since native P2WPKH went from 40% of all inputs in late 2022 to over 70% today. But that share kept climbing through 2026 while low-$r$ recovered from a low of 59.2% in September 2025 to 68.3% today, so the mix cannot be the story. Answering it properly needs per-signer data, which daily aggregates do not have. If you know, get in touch.
The numbers
Every ECDSA signature ever mined, up to 2026-08-16:
| signatures | share | |
|---|---|---|
| ECDSA signatures, total | 3,459,841,248 | |
| low-$r$ | 2,067,946,821 | 59.77% |
| high-$r$ | 1,391,894,427 | 40.23% |
| low-$s$ | 3,396,981,816 | 98.18% |
| high-$s$ | 62,859,432 | 1.82% |
And the bytes:
| saved | missed | |
|---|---|---|
| $r$ | 2,067,946,821 B $\approx$ 1.93 GiB | 1,391,894,427 B $\approx$ 1.30 GiB |
| $s$ | 3,396,981,816 B $\approx$ 3.16 GiB | 62,859,432 B $\approx$ 60 MiB |
| total | 5,464,928,637 B $\approx$ 5.09 GiB | 1,454,753,859 B $\approx$ 1.35 GiB |
So low values have kept 5.09 GiB off the chain, and another 1.35 GiB went on it that did not have to.
Year by year
| year | ECDSA signatures | low-$r$ | low-$s$ | bytes saved by low-$r$ |
|---|---|---|---|---|
| 2009 | 2,887 | 50.02% | 50.43% | 1,444 |
| 2010 | 191,116 | 50.03% | 50.16% | 95,608 |
| 2011 | 3,447,119 | 50.01% | 49.96% | 1,723,828 |
| 2012 | 17,736,693 | 50.00% | 49.99% | 8,868,495 |
| 2013 | 44,792,943 | 50.00% | 50.22% | 22,394,729 |
| 2014 | 70,930,099 | 50.03% | 65.92% | 35,488,821 |
| 2015 | 134,307,571 | 51.57% | 95.78% | 69,257,943 |
| 2016 | 234,039,416 | 50.68% | 100.00% | 118,612,031 |
| 2017 | 288,523,846 | 50.44% | 100.00% | 145,545,026 |
| 2018 | 258,365,252 | 50.43% | 100.00% | 130,300,030 |
| 2019 | 293,511,914 | 56.96% | 100.00% | 167,175,275 |
| 2020 | 327,550,787 | 63.51% | 100.00% | 208,038,031 |
| 2021 | 335,559,999 | 66.74% | 100.00% | 223,958,700 |
| 2022 | 331,825,716 | 68.29% | 100.00% | 226,614,538 |
| 2023 | 295,429,445 | 66.95% | 100.00% | 197,781,952 |
| 2024 | 303,704,931 | 62.53% | 100.00% | 189,894,515 |
| 2025 | 310,498,544 | 60.78% | 100.00% | 188,717,072 |
| 2026* | 209,422,970 | 63.74% | 100.00% | 133,478,783 |
* 2026 is partial, through August 16th.
Not all bytes weigh the same
Since SegWit, not every byte takes up the same amount of a block. A block holds 4,000,000 weight units: a byte in the base transaction costs 4 of them, a byte in the witness costs 1. Divide weight by four and you get vbytes, so a base byte is 1 vbyte and a witness byte is only 0.25.
That matters here because a signature in a legacy input sits in the scriptSig, which is base data, while a signature in a SegWit input sits in the witness.
mainnet.observer counts signatures but does not say which kind of input each one came from, so I estimate it: every day I split the signatures between scriptSig and witness in proportion to the legacy and SegWit v0 inputs spent that day. Taproot inputs are left out, since they carry Schnorr signatures, not ECDSA.3
So the 5.09 GiB saved is not 5.09 GiB of block space. Most of those were witness bytes:
| bytes | vbytes | |
|---|---|---|
| saved | 5,464,928,637 | 3,292,879,022 |
| missed | 1,454,753,859 | 969,157,898 |
A block holds one million vbytes and a new one arrives every ten minutes, which turns any pile of vbytes into an amount of time. The saved side comes to 3,293 blocks, about three weeks of chain. The missed side comes to 969 blocks, close to a week.
What is still on the table
That missed week is the number I keep coming back to. Counting only $r$, it is 906 blocks of block space, more than six days, sitting in 0x00 bytes that a second signing attempt would have removed.
It is not history either. High-$r$ signatures burned another 24.8 blocks during 2026 alone, and in August 2026 low-$r$ is at 68.3%, so roughly one signature in three still isn’t ground.
That share also says how much software grinds at all, which is the bleaker number. A wallet that grinds emits a low $r$ essentially every time; one that doesn’t lands there half the time by luck. So if $p$ is the share of signatures coming from grinders:
\[0.682 = p + \tfrac{1}{2}(1 - p) \implies p = 0.364\]Only about 36% of signatures come from software that grinds. The remaining two thirds are not wallets missing a byte here and there, they are wallets that never grind at all. Even at the December 2022 peak of 70.2%, grinders were barely over 40%.
Some of that is unfixable: old software, exotic signers, HSMs that will not re-sign. The rest is wallets that could ship a four-line change.
The grind nobody does
There is one more byte down there. Grind until $r < 2^{248}$, so the top byte of $r$ is zero and DER drops it entirely, and you save a second byte per signature: another 3.22 GiB across all history, about two thirds of everything low values have saved so far.
Nobody does it. The cost goes from ~2 signing attempts to ~256, and a wallet emitting 70-byte ECDSA signatures would be the loudest fingerprint on the chain. The byte you save is not always worth the identity you spend.
ECDSA signature grinding — what each byte costs
A simulation of the encoder: draw a nonce and watch what DER does with the resulting r. Each byte you shave off costs signing work, and eventually costs privacy.
How much of that was really grinding?
Everything above counts bytes that never reached the chain, and they are saved whatever the reason. But $r$ comes out low half the time by luck, so not all of them were paid for by anybody.
Say a year has 1,000 signatures and 700 are low-$r$. With nobody grinding you would still expect 500, so only the extra 200 are down to grinding:
\[\text{bytes from grinding} = \text{low-}r - \frac{\text{signatures}}{2}\]Over the whole history that gives 338,026,197 bytes, about 322 MiB. The other 1.6 GiB of low-$r$ savings would have happened on their own. The same maths on $s$, where the alternative to luck is negating, gives 1,667,061,192 bytes, about 1.55 GiB.
One catch: the formula credits grinding for any excess, including the excess luck put there. So the tool also measures how far the excess sits from chance. If every signature were a fair coin, that excess would have a standard deviation of $\sqrt{N}/2$; dividing one by the other gives a $\sigma$ count, and anything under 3 is what a coin does on a good day.
| period | bytes from grinding | $\sigma$ from chance |
|---|---|---|
| 2009–2013 | 0 | −0.3 |
| 2014 | 23,772 | 6 |
| 2015 | 2,104,158 | 363 |
| 2016 | 1,592,323 | 208 |
| 2017 | 1,283,103 | 151 |
| 2018 | 1,117,404 | 139 |
| 2019 | 20,419,318 | 2,384 |
| 2020 | 44,262,638 | 4,891 |
| 2021 | 56,178,700 | 6,134 |
| 2022 | 60,701,680 | 6,665 |
| 2023 | 50,067,230 | 5,826 |
| 2024 | 38,042,050 | 4,366 |
| 2025 | 33,467,800 | 3,799 |
| 2026* | 28,767,298 | 3,976 |
That column splits the history in three.
2009–2013 is luck. The excess never reaches one $\sigma$, and it is slightly negative, so those five years bought nothing at all.
2019 onward is grinding. Thousands of $\sigma$. There is no other way to read it.
2014–2018 I cannot explain. About 6 MiB over five years, but sitting at 139 to 363 $\sigma$, so it is not luck either, and it starts four years before v0.17.0. Something was already biased towards low $r$ before grinding shipped. Maybe a big service with its own signing code, maybe the signatures are less independent than a fair-coin model needs.
I would rather flag that than explain it away. What I will defend is the split: 98.2% of the 322 MiB comes from 2019 onward, once v0.17.0 was out.
Run it yourself
The tool is Rust, two small modules and two dependencies. It caches every CSV on the first run, so after that it works offline.
cargo build --release
# all-time summary
./target/release/bytes-saved-by-grinding-signatures
# since low-r grinding shipped, month by month
./target/release/bytes-saved-by-grinding-signatures --from 2018-10-01 --group month
# export for plotting
./target/release/bytes-saved-by-grinding-signatures --group month --csv > out.csv
Every figure here comes out of that binary, except the input-mix percentages in the sag paragraph, which I worked out separately. Code and methodology notes are in the repository.
So, was it worth it?
Grinding $r$ has bought Bitcoin 322 MiB of chain, about 159 blocks, in exchange for roughly doubling the ECDSA signing work of every wallet that does it. Negating $s$ cost nobody anything and bought five times more, in two years instead of eight.
That gap is the actual lesson, and it is not about bytes:
Free optimizations reach 100%. Optimizations that cost something stall at two thirds and then drift back down.
Low-$r$ grinding has been available, documented and free-as-in-code for eight years, and two thirds of Bitcoin’s ECDSA signatures still come from signers that don’t use it. If your wallet signs ECDSA and doesn’t grind, there are six days of block space with your name on it.
And Taproot? You can’t run any of this on it. A BIP-340 signature is a flat 64 bytes, 32 for $R_x$ and 32 for $s$, with no DER wrapper, no length prefixes and no sign bit to pad around.4 The 0x00 this whole post is about cannot exist there, so there is no low-$r$, no high-$r$ and nothing to grind.
Which makes the follow-up a different question, and a better one: not how many bytes wallets ground away, but how many bytes Bitcoin saved by making them impossible to waste. That’s the post I want to write next!
All data from mainnet.observer by 0xB10C, which does the hard part: parsing every block since 2009.
-
$n/2$ =
0x7FFF...5D576E7357A4501DDFE92F46681B20A0, comfortably under $2^{255}$. There is a razor-thin window between $n/2$ and $2^{255}$ where a high-$s$ value would still encode in 32 bytes, but it is about $2^{-128}$ of the range, so it never happens in practice. ↩ -
“At least”, because about one value in 200 is itself below $2^{248}$, so DER drops its leading zero too and that value saves two bytes instead of one. I count one every time, which makes every figure here a floor. Cross-checked against mainnet.observer’s signature-length counters, the shortfall is 0.52%. ↩
-
It is an approximation, and legacy multisig inputs, which carry several signatures each, bias the split. The byte counts are exact; the vbyte ones are not. ↩
-
65 bytes if the spender wants a sighash type other than
SIGHASH_DEFAULT, since BIP-341 appends the flag byte only in that case. Still fixed-length, still nothing to grind — though “did your wallet spend a byte writing an explicit0x01?” is arguably the Taproot-era descendant of this question. ↩
Enjoy Reading This Article?
Here are some more articles you might like to read next: