What Grinding Has Actually Saved on Chain

A few months ago, I wrote about whether you can fingerprint a hardware wallet from its signatures, and I discovered that some wallet vendors implement a subtle technique to save an almost free byte on each ECDSA signature.

That’s called low-r grinding. The r comes from one of the signature values, and grinding refers to repeatedly generating signatures until they manage to save that one byte.

Although I’ll explain how this optimization works later in this post, the first thing that came to mind when I learned about it was:

How many bytes has all that grinding actually saved?

One byte is a ridiculously small thing to care about. Yet Bitcoin Core shipped a feature to save it, and other wallets followed suit. So either everyone is wasting their time, or maybe, as we say in Catalonia, de mica en mica s’omple la pica or many a little makes a mickle.

Let’s count them ;)

Encoding and Signature Values

An ECDSA signature is transmitted in DER format:

30 <len> 02 <len_r> <r> 02 <len_s> <s> <sighash>

DER integers are signed and encoded using two’s complement. If the most significant bit of the first byte of $r$ or $s$ is set, the encoder prepends a 0x00 so the value isn’t read as negative.

A low value is one for which $r < 2^{255}$, or $s \leq n/2$. Such values would save one byte in a DER encoded signature.

How To Grind a Low Value

  how you get a low value cost
$s$ negate it: if $(r, s)$ is valid, so is $(r, n - s)$ 1 signature
$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.

$r$ comes from the nonce, $r = (kG)_x \bmod n$, that implies that a wallet that wants another value has to sign again. Core shipped that in v0.17.0 (October 2018).

The Accounting

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. I wanted to do it by myself with an owned node, but for now I’ll make the accounting with B10c data.

To do the accounting I wrote a small tool that pulls those series, caches them and does the accounting. It reports two numbers:

  • saved -> every low-$r$ and low-$s$ signature existing on chain.
  • missed -> every high-$r$ and high-$s$ signature existing on chain.
low-r low-s
40% 60% 80% 100% 50% Core v0.9.0 Core v0.11.1 Core v0.17.0 2010 2012 2014 2016 2018 2020 2022 2024 2026 low-s 100% low-r 68%

ECDSA signatures with a low r and a low s value, by month. Data: mainnet.observer.

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 more than 70% today. But that share kept growing through 2026 while the low-$r$ rate recovered from a low of 59.2% in September 2025 to 68.3% today, so the mix cannot be the explanation.

Answering it properly needs per-signer data, which, for obvious reasons, I don’t have. If you have another theory, get in touch.

Bytes Saved and Missed

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 could have been avoided.

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.

Since SegWit, not every byte takes up the same amount of a block. A block holds 4,000,000 weight units and 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 had to do an approximation. 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.

A BIP-340 signature is a flat 64 bytes, 32 for $R_x$ and 32 for $s$, with no DER wrapper. There is no padding byte to avoid, so there is nothing to save here.

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.

Counting only $r$, it is 906 blocks of block space, more than six days.

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.

How Much Of That Was Really Grinding?

Every low-$r$ signature saved a byte, but half of them would have been low anyway. The ones that count are the ones above that halfway line:

\[\text{signatures ground} = \text{low-}r - \frac{N}{2}\]

Out of $N = 3{,}459{,}841{,}248$ signatures, high-$r$ and low-$r$ together, chance alone would have made 1,729,920,624 of them low. The chain carries 2,067,946,821, so

\[2{,}067{,}946{,}821 - 1{,}729{,}920{,}624 = 338{,}026{,}197 \text{ signatures}\]

were made low deliberately. At one byte saved each that is 322 MiB, against the 1.93 GiB low-$r$ has saved in total.

\[\frac{338{,}026{,}197}{2{,}067{,}946{,}821} = 0.163\]

So grinding accounts for ~16% of the low-$r$ savings. The other 84% was going to happen with nobody doing anything.

We can also infer some information about wallets…

A wallet that grinds always ends up with a low $r$, because it keeps signing until it gets one. A wallet that does not grind gets a low $r$ half the time anyway, out of pure luck. So if nobody ground, the chain would sit at 50%. If everybody ground, it would sit at 100%. Anything in between is a mix of the two.

In August 2026 the chain sits at 68.3%. That is 18.3 points above the 50% that luck hands you for free, out of the 50 points that grinding could possibly add on top.

\[\frac{68.3 - 50}{100 - 50} = \frac{18.3}{50} = 0.366\]

So roughly 36% of signatures come from software that grinds.

This is a guess and not a measurement. It assumes a wallet either grinds every time or never grinds at all, and that the ones that don’t are a fair coin.

How to Grind Even a Little “Byte” More

There is actually one more byte we could save.

If we kept grinding until $r < 2^{248}$, the first byte of $r$ would be 0x00. DER could then drop it entirely, saving a second byte from every signature. Had this been done throughout Bitcoin’s history, it would have saved another 3.22 GiB, about two thirds of what low values have saved so far.

But! There is a reason nobody does it, and (I think) is not really the cost. I am not aware of any device that does this, and a wallet that was the only one doing it would sign every transaction with an unusually short, unusually rare signature. That makes it really easy to fingerprint, since anyone watching can tell which software produced it.

Finding a regular low-$r$ value takes about two signing attempts on average. Finding one below $2^{248}$ pins 8 more bits to zero (256 − 248), so it takes about $2^8 = 256$. This on your laptop would not be noticeable although I would want to try it on a hardware wallet just to check if the delay of signing a transaction would be important.

ECDSA signature grinding
— signature bytes
— signing attempts
DER structure r s padding / dropped

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.

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 :p

Writing this post made me wonder what the same analysis would look like for Schnorr signatures. There are far fewer of them than ECDSA signatures (and you’re not saving just 1 byte), so I suspect the numbers would be even more surprising!

All data from mainnet.observer by 0xB10C, which does the hard part: parsing every block since 2009.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Modifying the bdk_wallet Balance API for My Summer of Bitcoin Project
  • Is Hardware Wallet Fingerprinting Even Possible?
  • Covert-Nonce Channel Attacks on Bitcoin Hardware Wallets
  • Build Your Own Bitcoin Main-net