Skip to content

BIP Draft: Low-R Grinding for ECDSA Signatures - #2224

Open
liamgilligan wants to merge 1 commit into
bitcoin:masterfrom
liamgilligan:bip-low-r-grinding
Open

BIP Draft: Low-R Grinding for ECDSA Signatures#2224
liamgilligan wants to merge 1 commit into
bitcoin:masterfrom
liamgilligan:bip-low-r-grinding

Conversation

@liamgilligan

Copy link
Copy Markdown

Informational BIP describing low-R grinding: re-deriving the ECDSA nonce under a counter until r has its high bit unset, so the DER encoding needs no leading null byte.

This was discussed on the mailing list: https://groups.google.com/g/bitcoindev/c/boEZRlqczvw

Reference implementation and test vectors are TODO.

Feedback is very welcome.

Informational BIP describing low-R grinding: re-deriving the ECDSA
nonce under a counter until r has its high bit unset, so the DER
encoding needs no leading null byte.
@jonatack

Copy link
Copy Markdown
Member

@murchandamus murchandamus changed the title Add BIP bip-low-r-grinding: Low-R Grinding for ECDSA Signatures BIP Draft: Low-R Grinding for ECDSA Signatures Jul 28, 2026
@murchandamus

Copy link
Copy Markdown
Member

Thanks, adding this to my to-read list.

@craigraw

Copy link
Copy Markdown
Contributor

Another important motivation is that signatures produced according to a standardised approach will be byte-identical. This means signers can be compared to ensure they are not leaking secrets by embedding them in signatures (e.g. Dark Skippy).

@murchandamus murchandamus left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks very good. I just have a couple nits for you.

Comment thread bip-low-r-grinding.md
The *s* value of a signature can be normalized through simple negation, resulting in an equally valid signature whose *s* value has its most significant bit unset. A signature's *r* value cannot be modified in a similar way: it is the x-coordinate of the nonce point, and is fixed once the nonce is chosen. Its most significant bit is set about half the time.
Thus, about half of ECDSA signatures carry an extra padding byte when DER encoded.

The only way to obtain a different *r* is to re-sign with a different nonce. Low-R Grinding takes two signing operations on average[^geometric] and saves roughly 0.5 vbytes per signature in legacy inputs and 0.125 vbytes in v0 segwit inputs.[^optech]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When it is needed it saves a whole byte, so maybe add “in average”:

Suggested change
The only way to obtain a different *r* is to re-sign with a different nonce. Low-R Grinding takes two signing operations on average[^geometric] and saves roughly 0.5 vbytes per signature in legacy inputs and 0.125 vbytes in v0 segwit inputs.[^optech]
The only way to obtain a different *r* is to re-sign with a different nonce. Low-R Grinding takes two signing operations on average[^geometric] and saves in average roughly 0.5 vbytes per signature in legacy inputs and 0.125 vbytes in v0 segwit inputs.[^optech]

Comment thread bip-low-r-grinding.md

The only way to obtain a different *r* is to re-sign with a different nonce. Low-R Grinding takes two signing operations on average[^geometric] and saves roughly 0.5 vbytes per signature in legacy inputs and 0.125 vbytes in v0 segwit inputs.[^optech]

Combined with normalizing *s*, Low-R Grinding lets a signer produce ECDSA signatures for arbitrary messages with a fixed *maximum* size: 70 bytes of DER, 71 with the sighash flag. This allows for lower fees and more accurate transaction size predictions. Smaller *r* values could be ground for in theory, but are not in Low-R Grinding.[^pr13666]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also mention that for 2-of-3 P2SH inputs, having at least one high-r signature increases the input by an additional 2 bytes because the input script’s length indicator then requires three bytes. For 2-of-3 P2SH inputs, roughly 25% of inputs have two low-r signatures and take 293 bytes, about 50% have one high-r signature and take 296 bytes, and 25% of inputs have two high-r signatures that take 297 bytes. So for 2-of-3 P2SH inputs, the expected savings of low-r grinding is 2.5 vB.

@murchandamus

murchandamus commented Aug 3, 2026

Copy link
Copy Markdown
Member

The document should have a Rationale section. The Rationale should describe design decisions, compare to potential alternate designs and related work. You could mention the suggestion that Craig gave there. The explanation why incrementing a counter on top of RFC6979 is safe and recommend could also be located in that section. Some of the Footnotes would fit there as well, in fact, many BIPs use the Footnotes section as the Rationale.

@jeanpablojp

Copy link
Copy Markdown
Contributor

Since the reference implementation and test vectors are still marked TODO, I'd like to offer help there, if you aren't already deep into them.

What I have in mind is an independent Python implementation of the recommended instantiation: pure Python secp256k1, RFC 6979 with the grind counter in the additional data, the grinding loop, and DER encoding, plus a script that generates the vectors and a separate one that verifies them, everything checked byte for byte against Bitcoin Core's signer. I built the spend-path vectors for the BIP 360 draft recently (#2232), so this is familiar ground.

The reason I think an implementation that shares no code with libsecp is worth having: if it agrees with Core on the vectors, that's good evidence the text is unambiguous, and if it doesn't, the first diverging intermediate value shows exactly which part needs tightening.

If that sounds useful, two things worth deciding up front:

  1. Should the vectors carry intermediate values (additional data, nonce, r, s before low-s normalization, final DER), or only key, message hash, counter and signature? My preference is the full set: the keys are test-only so there's nothing to protect, and the intermediates are what lets two implementations find where they diverge. RFC 6979 publishes k in its own vectors.
  2. A JSON file next to the document, or vectors inline in the markdown?

Happy to open the result as a PR against your branch, or just post it here, whichever works better for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants