BIP draft: Half-Aggregation of BIP 340 Signatures #2205

pull fjahr wants to merge 2 commits into bitcoin:master from fjahr:halfagg changing 26 files +2290 −0
  1. fjahr commented at 7:58 PM on June 28, 2026: contributor

    This is the BIP draft for half-aggregation of BIP340 Schnorr signatures, which has marinated at https://github.com/BlockstreamResearch/cross-input-aggregation/blob/master/half-aggregation.mediawiki long enough.

    Mailing list discussion: https://gnusha.org/pi/bitcoindev/33f275c2-06b1-4b4a-2a75-cafe36836503@gmail.com/

    Compared to the master version of the BIP linked above and the state of things at the time of sharing this on the mailing list, this version has a few minor typo fixes as well as three significant changes:

    1. Removal of a max signatures limit, see https://github.com/BlockstreamResearch/cross-input-aggregation/pull/16
    2. Replacement of the reference implementation in hacspec with one in Python, based on secp256k1lab. See https://github.com/BlockstreamResearch/cross-input-aggregation/pull/21 which also adds the test vectors

    Given the Python reference implementation, I am vendoring the latest master of secp256k1lab with the BIP, as seems to be the standard with other BIPs (seen merged at 374 and in the pull for 445).

  2. bip-XXXX: Add vendored secp256k1lab library
    Add a vendored copy of the secp256k1lab library (master branch, commit
    a265da139aea27386085a2a8760f8698e1bda64e) that the half-aggregation
    reference implementation and test vector scripts depend on.
    67dbf011e0
  3. jonatack added the label New BIP on Jun 28, 2026
  4. in bip-XXXX.mediawiki:26 in 6bc6c4e60e
      21 | +Half-aggregation is a non-interactive process for aggregating a collection of signatures into a single aggregate signature.
      22 | +The size of the resulting aggregate signature is approximately half of the combined size of the original signatures.
      23 | +
      24 | +=== Copyright ===
      25 | +
      26 | +This document, along with the reference implementation and test vectors in the [[bip-XXXX/|bip-XXXX directory]], is licensed under the BSD-3-Clause License. The exception is the vendored copy of the secp256k1lab library in [[bip-XXXX/secp256k1lab/|bip-XXXX/secp256k1lab]], which is licensed under the MIT License as stated in its [[bip-XXXX/secp256k1lab/COPYING|COPYING]] file.
    


    jonatack commented at 8:18 PM on June 28, 2026:

    There is a placeholder link here to the reference implementation; should it specify bip-XXXX/halfagg.py?


    fjahr commented at 10:30 AM on June 29, 2026:

    Replaced with the direct links

  5. in bip-XXXX.mediawiki:22 in 6bc6c4e60e outdated
      17 | +
      18 | +=== Abstract ===
      19 | +
      20 | +This document describes ''half-aggregation'' of BIP 340 signatures.
      21 | +Half-aggregation is a non-interactive process for aggregating a collection of signatures into a single aggregate signature.
      22 | +The size of the resulting aggregate signature is approximately half of the combined size of the original signatures.
    


    jonatack commented at 8:31 PM on June 28, 2026:

    Feel free to ignore, to me at first glance it might be useful to readers to reference BIPs 327-328, for instance, to contrast the difference.


    fjahr commented at 10:29 AM on June 29, 2026:

    I felt like this doesn't really belong here but I have also seen plenty of people getting confused by this, so I have added little content on the contrast to MuSig. Added in motivation since that seemed the most fitting place.

  6. jonatack commented at 8:32 PM on June 28, 2026: member

    Looks quite complete. Might be good to add a backward compatibility section (if not needed, that could be explicitly stated).

  7. jonatack added the label Needs number assignment on Jun 28, 2026
  8. fjahr referenced this in commit d0ba26aa65 on Jun 28, 2026
  9. fjahr referenced this in commit 653d368158 on Jun 28, 2026
  10. fjahr referenced this in commit bb1f2afc43 on Jun 28, 2026
  11. fjahr force-pushed on Jun 29, 2026
  12. fjahr commented at 10:32 AM on June 29, 2026: contributor

    Addressed your comments @jonatack , thanks!

    Might be good to add a backward compatibility section (if not needed, that could be explicitly stated).

    I added a "section not needed" section ;)

  13. in bip-XXXX/run_test_vectors.py:67 in 22a1686728
      62 | +            if expected_result == "FALSE":
      63 | +                try:
      64 | +                    Aggregate(pms)
      65 | +                    failures.append(f"Aggregate #{index}: {comment}")
      66 | +                except:
      67 | +                    pass
    


    theStack commented at 3:08 PM on June 29, 2026:

    here and in the other similar instances below: I think swallowing all exceptions in the test runner is problematic, or could at the very least be annoying; for example, I played a bit around in halfagg.py adding debug messages and noticed that syntax errors / typos I made (leading to e.g. a NameError) wouldn't even show up any more. In this case explicitly catching ValueError seems to be sufficient, as any other exception would indicate something is wrong with the reference implementation?


    fjahr commented at 8:06 PM on June 30, 2026:

    Right, sounds good to me, done.

  14. in bip-XXXX.mediawiki:98 in 22a1686728 outdated
      93 | +
      94 | +The following pseudocode is ''not'' a specification but is only intended to augment the actual Python [[#specification|specification]].
      95 | +
      96 | +==== Notation ====
      97 | +
      98 | +The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme<ref>Among other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.</ref>.
    


    theStack commented at 3:16 PM on June 29, 2026:

    Fwiw, in BIP-445 a different notation is used (for the first time, as far as I'm aware) that leans more to secp256k1lab, with a table showing how the notation maps to the lab functions: https://github.com/siv2r/bips/blob/bip-frost-signing/bip-0445.md?plain=1#L255

    (I found that quite a nice idea for improved readability and hopefully easier navigation through the reference implementation; but that's of course more a nit than a requirement; ultimately it's up to the BIP authors).


    fjahr commented at 8:09 PM on June 30, 2026:

    Thanks, I didn't quite pick up on this despite looking at the BIP445 draft for some inspiration. But I am not sure it's the best idea to adapt this notation fully here. I think there is also some value in having the notation closer to BIP340 which is a prerequisite to this BIP here while BIP445 is not directly related. I did steal the idea of the mapping table though, please let me know if you agree with this decision and think the mapping table on it's own is helpful.

  15. in bip-XXXX/halfagg.py:95 in 22a1686728
      90 | +        r_values.append(ri)
      91 | +
      92 | +        # Let si = int(sigi[32:64]); fail if si ≥ n
      93 | +        si = int_from_bytes(sigi[32:64])
      94 | +        if si >= n:
      95 | +            raise ValueError("si must be less than n")
    


    theStack commented at 3:26 PM on June 29, 2026:

    nit: here an in a few other instances: instead of bare integers, could use secp256k1lab's Scalar type (using its .from_bytes_{checked,wrapping} and .to_bytes() methods); ultimately if that's done everywhere, I think manual int_from_bytes/bytes_from_int calls and modulo n reductions are not even needed anymore


    fjahr commented at 8:09 PM on June 30, 2026:

    Done, thanks!

  16. in bip-XXXX/halfagg.py:146 in 22a1686728
     141 | +        # Let (pki, mi) = pm_aggdi
     142 | +        (pki, mi) = pm_aggd[i]
     143 | +
     144 | +        # Let Pi = lift_x(int(pki)); fail if that fails
     145 | +        try:
     146 | +            Pi = GE.lift_x(int_from_bytes(pki))
    


    theStack commented at 3:32 PM on June 29, 2026:

    nit: could use

                Pi = GE.from_bytes_xonly(pki)
    

    here (and for Ri below)


    fjahr commented at 8:10 PM on June 30, 2026:

    Done, thanks!

  17. in bip-XXXX/halfagg.py:41 in 22a1686728
      36 | +    # Let aggsig = bytes(0)
      37 | +    aggsig = bytes([0] * 32)
      38 | +
      39 | +    pm_aggd = []
      40 | +    if not VerifyAggregate(aggsig, pm_aggd):
      41 | +        raise
    


    theStack commented at 3:33 PM on June 29, 2026:

    this seems not strictly necessary (could assert instead if it's still desired to keep it as an internal sanity check, I guess?)


    fjahr commented at 8:11 PM on June 30, 2026:

    Right, I removed it.

  18. theStack commented at 4:01 PM on June 29, 2026: contributor

    Nice! Left some notation and style nits below (mostly related to secp256k1lab usage).

  19. BIP draft: Add half-aggregation for Schnorr signatures 5378d373fe
  20. fjahr force-pushed on Jun 30, 2026
  21. fjahr commented at 8:12 PM on June 30, 2026: contributor

    Addressed feedback from @theStack , thanks a lot!


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-07-01 10:10 UTC

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