Is not demanding low-R grinding still relevant for external signers? #33761

issue cobratbq openend this issue on November 1, 2025
  1. cobratbq commented at 7:18 pm on November 1, 2025: none

    Is there an existing issue for this?

    • I have searched the existing issues

    Current behaviour

    It seems that largest transaction coming from (my) external signer is not accepted in mempool. From what I understand, low-R grinding is not expected from external signers. That’s what I understand https://github.com/bitcoin/bitcoin/issues/26030.

    Is this still true? (it is desirable) This is the result for me in ‘regtest’ environment on Bitcoin Core v29.2.

    Expected behaviour

    Any transaction properly signed by external signer (because potentially low-capacity embedded device) is accepted.

    Steps to reproduce

    Connect “external signer” via External Signer interface (command-line tool, directly, not through HWI)

    Sign transaction until transaction with largest size is encountered.

    Relevant log output

    Three transactions. Largest is not accepted for me on ‘regtest’, and to my knowledge signature is good.

    0020000000122d24e014ba0566c52dcedc316116fc6c0506d8d4506a59df48c086dafa944be010000006a47304402203d2e4ad14e4ffde063fab547d074d842060054827dd2db26b6b77ac40faa6aec022014de31cfda5fbe61a789112e106cc07a13a6187682908b22ec28f5b41256dd80012103158a5f4fd7588aa696a13d49889895b4b077aae721b52f9b72cda919ff926dfffeffffff02d2d4e405000000001600143e445eb2b84a73fec1851628a8f20998880af04d00c39dd0000000001976a914d2d44c8765409c6cecb3eb0861f7beb78f232c9e88ac00000000
    
    00200000001b0a283e1c1634f1eb748cb1a7ca0c6bda1d385037a6a626cdf3aa0b865a1be9e000000006b483045022100d412738cb2a54c530f62ba4f2458660dfab5239297df074bf5bb829f369c4c70022077f6cd209178e9aff43269c42c8df34c710096aa88c8a77d8f860db218603e050121031d633694c1ba15687ff4c1eb9ecb5ec730d44131008fa250d49e5f80dc516379feffffff0288dde405000000001600143e445eb2b84a73fec1851628a8f20998880af04d00a493d6000000001976a91418a4d1bec02c5489bebdd6fbb304e8e1b173733588ac00000000
    
    0020000000122d24e014ba0566c52dcedc316116fc6c0506d8d4506a59df48c086dafa944be010000006c493046022100d21e82e9f292654f52bb3623f803b07e74c3b91f55aad40b6d0f5d767f2b66c9022100826102337dd415d4f9adcb4874da63f7981ffcaff18f0c2a94715d9ea3596c0d012103158a5f4fd7588aa696a13d49889895b4b077aae721b52f9b72cda919ff926dfffeffffff0288dde405000000001600143e445eb2b84a73fec1851628a8f20998880af04d00c39dd0000000001976a914d2d44c8765409c6cecb3eb0861f7beb78f232c9e88ac00000000
    

    How did you obtain Bitcoin Core

    Package manager

    What version of Bitcoin Core are you using?

    v29.2.0

    Operating system and version

    Fedora

    Machine specifications

    No response

  2. achow101 commented at 6:05 pm on November 2, 2025: member

    What is the exact error that you are getting?

    There is no “demanding”, we cannot demand or request external signers do anything with low-R grinding. All we can do is make a guess about whether low-R grinding will be done, and account for that when selecting inputs so that the fee can be set appropriately. The current behavior is that if an input can be signed by the wallet itself, then we assume low-R grinding will occur. If it cannot be, i.e. the wallet does not have the private keys to sign the input, then we assume that low-R grinding will not occur. The only effect of this should be that sometimes a transaction will pay a slightly higher feerate than specified. It should not cause a transaction to not be accepted to the mempool.

  3. cobratbq commented at 6:15 pm on November 2, 2025: none

    What is the exact error that you are getting?

    Not error. Every time a transaction is signed and ready in the transaction list, when the transaction is of the largest flavor, it is completely greyed out and status tells me “unconfirmed, not in memory pool”. I cannot find any other explanation why it is always the largest variant (maximum bytes) that is rejected by the memory pool.

    Note: this is on regtest, so might it be that memory pool rejects it by some average number that is badly determined because there is no (globally determined) average?

    My intuitive guess is that the transaction is signed with a predetermined fee that assumes that either R or s is slightly smaller, hence not accepted by memory pool. To my knowledge, the signature is correct, as it is always processed in the same way regardless of size.

    There is no “demanding”, we cannot demand or request external signers do anything with low-R grinding. All we can do is make a guess about whether low-R grinding will be done, and account for that when selecting inputs so that the fee can be set appropriately.

    The fee is guessed beforehand, right? So is it possible that the memory pool rejects the largest transaction because the guess is for a fee for 1 byte less? Or does that not matter for being accepted by the mempool, just that the transaction is slightly less attractive for slightly lesser over-all fee?

    The current behavior is that if an input can be signed by the wallet itself, then we assume low-R grinding will occur. If it cannot be, i.e. the wallet does not have the private keys to sign the input, then we assume that low-R grinding will not occur. The only effect of this should be that sometimes a transaction will pay a slightly higher feerate than specified. It should not cause a transaction to not be accepted to the mempool.

    When you say “wallet”, I presume you mean Bitcoin Core in possession of private key in own management? If so, the signer is configured and registered as external signer, and during creation the external signer checkbox is thus also set.

  4. achow101 commented at 6:28 pm on November 2, 2025: member

    Can you try to broadcast the problematic transaction using sendrawtransaction? It should give you a more specific error.

    The fee is guessed beforehand, right?

    The fee is set beforehand, the size is guessed.

    So is it possible that the memory pool rejects the largest transaction because the guess is for a fee for 1 byte less?

    Yes. If you set the feerate to be minrelayfee, and the transaction is larger than was estimated, then the actual feerate will be lower than minrelayfee and the transaction won’t be accepted.

    When you say “wallet”, I presume you mean Bitcoin Core in possession of private key in own management?

    Yes.


    In the 3 transactions you posted, the first one is 222 bytes, the second 223, and the last 224. Of those 3 transactions, the 222 byte has a signature with low-R low-S, the 223 is high-R low-S, and the 224 is high-R high-S.

    From your description, the 224 bytes transaction is the one that isn’t being accepted? In that case, it is because high-S has been non-standard for a very long time now. That itself is an issue. When the wallet does size estimation, it always assumes a low-S because high-S itself won’t be accepted by the mempool.

    You need to work with the creator of your external signing tool to have it make signatures with only low-S.

  5. cobratbq commented at 7:04 pm on November 2, 2025: none

    Can you try to broadcast the problematic transaction using sendrawtransaction? It should give you a more specific error.

    Will try.

    [..]

    So is it possible that the memory pool rejects the largest transaction because the guess is for a fee for 1 byte less?

    Yes. If you set the feerate to be minrelayfee, and the transaction is larger than was estimated, then the actual feerate will be lower than minrelayfee and the transaction won’t be accepted.

    Not sure if this applies in ‘regtest’. I’ll see if I can query the ‘minrelayfee’ via the console or something.

    [..]

    In the 3 transactions you posted, the first one is 222 bytes, the second 223, and the last 224. Of those 3 transactions, the 222 byte has a signature with low-R low-S, the 223 is high-R low-S, and the 224 is high-R high-S.

    Correct.

    From your description, the 224 bytes transaction is the one that isn’t being accepted? In that case, it is because high-S has been non-standard for a very long time now. That itself is an issue. When the wallet does size estimation, it always assumes a low-S because high-S itself won’t be accepted by the mempool.

    Okay, so it is most likely about high-S.

    Any ref of high-S / low-S?

    I started from a plain embedded ECDSA p256r1 library, so any necessary adjustments I make myself.

    I’d guess it’s about something like clearing a highest bit somewhere, which then becomes the sign bit in the ASN.1-encoded DER-encoding. I presume there is some resource that evaluates the loss of randomness in this activity or otherwise clarifies why this is chosen.

    You need to work with the creator of your external signing tool to have it make signatures with only low-S.

    I am the dev. (It’s an open source project.)

  6. achow101 commented at 7:14 pm on November 2, 2025: member

    Any ref of high-S / low-S?

    https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#user-content-Low_S_values_in_signatures (the BIP itself is withdrawn, but this section on low-S is relevant in this dicsussion)

  7. cobratbq commented at 9:22 pm on November 2, 2025: none

    This seems to do the job. Right, so high-s was the problem. Given that it produces signatures of both 222 bytes and 223 bytes, high R must indeed (still) be accepted.

    So, essentially s' = n - s with n being the order of the curve. Except that your reference writes out the value of n itself.

  8. cobratbq closed this on Nov 2, 2025


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-12-07 09:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me