{
 "number": 29491,
 "repo": "bitcoin/bitcoin",
 "url": "https://github.com/bitcoin/bitcoin/pull/29491",
 "title": "[EXPERIMENTAL] Schnorr batch verification for blocks",
 "author": "fjahr",
 "author_association": "MEMBER",
 "created_at": "2024-02-27T17:06:26Z",
 "updated_at": "2026-09-11T02:15:28Z",
 "age_days": 932,
 "draft": true,
 "labels": [],
 "milestone": null,
 "base": "master",
 "head_sha": "c4c9aa200d341377fe528de1e30d04fe370c1e09",
 "head_ref": "2024-02-batch-validation-updated",
 "head_repo": "fjahr/bitcoin",
 "head_history": [
  {
   "t": "2024-02-27T17:10:54Z",
   "sha": "0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d"
  },
  {
   "t": "2024-02-28T13:14:03Z",
   "sha": "1825555b6b8d8030e747eda82ddab4b15a87071d"
  },
  {
   "t": "2024-12-05T13:57:12Z",
   "sha": "6f271d7bd0926a926b643f60f65263bed1d07c44"
  },
  {
   "t": "2025-03-11T23:43:41Z",
   "sha": "cafdd148f6bd2206da1bf88d684b82feea47f65d"
  },
  {
   "t": "2025-03-23T22:44:42Z",
   "sha": "4ab16a0b2801023d5656ec9a0cb9cfdeeeb1a029"
  },
  {
   "t": "2025-05-18T23:57:21Z",
   "sha": "4d9834e80cfd95795e9cca53a4cff75d22c7f3ad"
  },
  {
   "t": "2025-07-24T15:30:21Z",
   "sha": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6"
  },
  {
   "t": "2026-01-26T15:56:25Z",
   "sha": "452a3754bf77adb7484d789469973f05d4c0e692"
  },
  {
   "t": "2026-01-27T12:53:33Z",
   "sha": "3029493ef1e1a07995304b08001225f9fda6e882"
  },
  {
   "t": "2026-01-27T16:19:15Z",
   "sha": "c5a124f429d7447bf08d2d82a8af5b294acfa759"
  },
  {
   "t": "2026-01-27T17:00:25Z",
   "sha": "3979539ced5afc82a7858538ca9c49a5cd16cc43"
  },
  {
   "t": "2026-01-29T15:42:08Z",
   "sha": "46d1cf7efe2019e379bf2ebc1e4f59701d4d111e"
  },
  {
   "t": "2026-01-29T16:19:23Z",
   "sha": "41e0ea662abdc5259ee05080f4e0a037a38e5d4c"
  },
  {
   "t": "2026-01-31T14:00:06Z",
   "sha": "f178c6cba7d179308324ff4a21b7ff20fc09ec2c"
  },
  {
   "t": "2026-07-31T15:34:01Z",
   "sha": "310df214a427821918b0e34951fea250213fd338"
  },
  {
   "t": "2026-08-01T08:32:29Z",
   "sha": "572960d8804421b8656fcdb2b989acc408f342e8"
  },
  {
   "t": "2026-08-01T09:18:01Z",
   "sha": "c4c9aa200d341377fe528de1e30d04fe370c1e09"
  }
 ],
 "additions": 2739,
 "deletions": 182,
 "changed_files": 56,
 "commit_count": 11,
 "size_bucket": "XL",
 "mergeable_state": "clean",
 "bot": {
  "drahtbot": {
   "present": true,
   "reviews": {},
   "conflicts": [
    {
     "number": 35793,
     "title": "Implement BIP 54 (Consensus Cleanup) without mainnet activation",
     "author": "darosior"
    },
    {
     "number": 35744,
     "title": "coins: prevent DB resize from invalidating cursors",
     "author": "l0rinc"
    },
    {
     "number": 35662,
     "title": "script: prevent stale sighash caches across transactions",
     "author": "l0rinc"
    },
    {
     "number": 35569,
     "title": "Encapsulation for CTransaction",
     "author": "purpleKarrot"
    },
    {
     "number": 35511,
     "title": "RFC: consensus: Make `CAmount` a class",
     "author": "hodlinator"
    },
    {
     "number": 32575,
     "title": "consensus: Remove special treatment for single threaded script checking",
     "author": "fjahr"
    },
    {
     "number": 30342,
     "title": "kernel, logging: Pass Logger instances to kernel objects",
     "author": "ryanofsky"
    },
    {
     "number": 29843,
     "title": "policy: Allow non-standard scripts with -acceptnonstdtxn=1 (test nets only)",
     "author": "ajtowns"
    },
    {
     "number": 29247,
     "title": "CAT in Tapscript (BIP-347)",
     "author": "arminsabouri"
    },
    {
     "number": 28690,
     "title": "build: Introduce internal kernel library",
     "author": "sedited"
    }
   ]
  }
 },
 "acks_parsed": {},
 "acks_tally": {
  "ack": 0,
  "stale_ack": 0,
  "concept_ack": 0,
  "approach_ack": 0,
  "nack": 0,
  "concept_nack": 0,
  "approach_nack": 0
 },
 "reviews": {
  "approved": 0,
  "changes_requested": 0,
  "distinct_reviewers": [
   "0xB10C",
   "Eunovo",
   "Sjors",
   "andrewtoth",
   "willcl-ark"
  ]
 },
 "signals": {
  "needs_rebase": false,
  "ci_failed": false,
  "mergeable_state": "clean",
  "last_author_activity": "2026-08-01T09:18:01Z",
  "last_reviewer_activity": "2025-06-15T19:32:11Z",
  "last_reviewer": "Eunovo",
  "author_silent_days": 47,
  "waiting_on_author_days": 0,
  "days_since_update": 6
 },
 "refs": {
  "mentioned": [
   26045,
   31689
  ],
  "depends_on": [],
  "fixes": [],
  "linked_issues": [],
  "references": [
   {
    "number": 26045,
    "type": "pull",
    "state": "closed",
    "merged": false,
    "merged_at": null,
    "title": "rpc: Optimize serialization disk space of dumptxoutset"
   },
   {
    "number": 31689,
    "type": "pull",
    "state": "closed",
    "merged": true,
    "merged_at": "2025-03-23",
    "title": "Benchmark Chainstate::ConnectBlock duration"
   }
  ],
  "conflicts": [
   35793,
   35744,
   35662,
   35569,
   35511,
   32575,
   30342,
   29843,
   29247,
   28690
  ]
 },
 "stack": {
  "shares_commits_with": [],
  "based_on": [],
  "base_for": []
 },
 "review_paths": [
  "src/batchverify.cpp",
  "src/checkqueue.h",
  "src/script/sigcache.h"
 ],
 "body": "This is a draft implementation of Schnorr signature batch verification in blocks, put here for conceptual discussion, and collaborative benchmarking.\n\nThe `secp256k1` code is still under review itself, please spend some time giving feedback there if you can:\n- [Batch module PR](https://github.com/bitcoin-core/secp256k1/pull/1134)\n- [ecmult refactoring PR](https://github.com/bitcoin-core/secp256k1/pull/1789)\n\nTODOs\n- [ ] Batch taproot tweak verification as well",
 "commits": [
  {
   "sha": "6885e17bab3876c85bae5ef35eebe805787fad61",
   "date": "2026-07-31T13:50:05Z",
   "message": "Squashed 'src/secp256k1/' changes from d2d04864ef9..422a1d5c5f5\n\n422a1d5c5f5 batch: pass hash_ctx to sha256 calls after pluggable-compression rebase\nbdee212d797 batch: make add functions void & introduce reset\n204918399cd batch: remove `batch_usable` api\n1bee1a7454f batch: make tests functions internal & static\n2ffe7deb903 fix typos & index the right inputs for benchmarks\nd7f64a4d719 batch: remove experimental status\ncd8e881846d test: fix ci failures\n5b8d3f81f9f batch: Generate speedup graphs\n3f2b13a13d4 batch, extrakeys: Add benchmarks\nb512ba56920 batch: Add tests for batch_add_* APIs\n862a97a19b5 batch,ecmult: Add tests for core batch APIs and strauss_batch refactor\n846b71de8c6 batch: Add example\n7af8036b916 batch: Add batch_add_* APIs\nabb28605ac3 batch, ecmult: Add batch_verify and refactor strauss_batch\nccdbf7e0c4f batch: Add create and destroy APIs\nf12ec74d586 batch: Initialize an experimental batch module\n\ngit-subtree-dir: src/secp256k1\ngit-subtree-split: 422a1d5c5f55ddd83b91e1da119563e911bb72aa"
  },
  {
   "sha": "07bcd03eed0fbe2b4d32853ae10497b97f2d10b7",
   "date": "2026-07-31T13:50:05Z",
   "message": "Update secp256k1 subtree to include batch verification module\n\nPulls in bitcoin-core/secp256k1#1134 (batch verification module) rebased\non top of the secp256k1 commit currently vendored in master, so the only\nchange to the subtree is the addition of the batch module."
  },
  {
   "sha": "4ce833a75bcd8393f26442b3f0a1d81d29158535",
   "date": "2026-07-31T21:12:02Z",
   "message": "validation: Add BatchVerify (unused)"
  },
  {
   "sha": "174c68dc46174212535a87c18632c529210ec034",
   "date": "2026-07-31T21:12:03Z",
   "message": "script: Add batch validation error"
  },
  {
   "sha": "41580d47982afa241402b8066336043559d9f17c",
   "date": "2026-08-01T09:14:48Z",
   "message": "sigcache: Add CollectingSignatureChecker"
  },
  {
   "sha": "54bb0b4d4b8703e8fc88d34d98fd2c9c6c96417c",
   "date": "2026-08-01T09:14:48Z",
   "message": "validation: Add BatchableScriptChecker"
  },
  {
   "sha": "cbb03d5ec0d41f151201059d895fa71b5c0248c7",
   "date": "2026-08-01T09:14:48Z",
   "message": "validation: Use schnorr signature batch validation in parallel script verification\n\nCo-authored-by: Eunovo <eunovo9@gmail.com>"
  },
  {
   "sha": "0c33117a56f29a529f8b4db0289fd4352063a9ab",
   "date": "2026-08-01T09:14:49Z",
   "message": "validation: Use schnorr batch validation in single thread case"
  },
  {
   "sha": "71f8b7b26239b8f7f96fac62fb764ea902067e25",
   "date": "2026-08-01T09:14:49Z",
   "message": "fuzz: Add basic fuzz test for BatchSchnorrVerifier"
  },
  {
   "sha": "61163de9d15a22342a753454bf9caf7fff391b48",
   "date": "2026-08-01T09:14:49Z",
   "message": "test: Add BatchSchnorrVerifier unit test"
  },
  {
   "sha": "c4c9aa200d341377fe528de1e30d04fe370c1e09",
   "date": "2026-08-01T09:14:49Z",
   "message": "test: Add functional test for unspendable taproot output key"
  }
 ],
 "timeline": [
  {
   "t": "2024-02-27T17:10:54Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d"
  },
  {
   "t": "2024-02-28T09:40:46Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "I guess this is most interesting to test against the last 50K blocks on mainnet, i.e. since early 2023? Given the high percentage of taproot transactions. Such testing could benefit from a assume utxo snapshot, saving everyone the pain of rewinding. I can bake one, though ideally I'd like #26045 to land first.\n\nIs the current PR already using batches? It's unclear to me by glancing over `validation.cpp` if and how it's collecting up to `max_batch_size{106}` signatures before calling `batch.Verify()`.\n\nMaybe add a `-schnorrbatchverify` startup argument so more people can try it once it gets through review."
  },
  {
   "t": "2024-02-28T12:53:21Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nYeah, that would be great. I have to get the tracing part to work in my setup again before I can start.\n\n[quoted text omitted]\nYes, the rough architecture is as as follows (sorry for not writing a better explanation above but it's still very rough and I expect it to change). So, the [`batch` object is constructed](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R2401) and then [handed down from `ConnectBlock()` to `CheckInputScripts()`](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R2454) which [hands it to the `CScriptCheck` constructor](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R1961). When the [`CScriptCheck` instance is executed](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R1863) and the batch object is present [`BatchingCachingTransactionSignatureChecker` is used](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-0618dae2990d096e96e0283f7ab8cee069469f1ce603b58c0bb289e154f3aa17R40) which only differs from the default `CachingTransactionSignatureChecker` in that its [implementation of `VerifySchnorrSignature()`](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-612abe4a74f2e9f6903cebff057d47bfe4f8a57507c175743f4b62fde0d3cc58R132) and adds the Schnorr sig to the `batch` object instead of verifying it right there. (Here we have an issue because the function is not doing what the name says anymore but it is a much simpler change this way and I find it hard to predict where we will land with this in the end.) Then [back in `ConnectBlock()` the `batch` object is verified](https://github.com/bitcoin/bitcoin/pull/29491/commits/0a5e6128680c8b4f43fcb2d0450d41d5e4603c9d#diff-97c3a52bc5fad452d82670a7fd291800bae20c7bc35bb82686c2c0a4ea7b5b98R2489) after all transactions have been iterated over.\n\nThe 106 is an implementation detail from `secp256k1` that I am not sure needs to be really exposed here. But what matters for the question is: if there are more than 106 sigs added to the batch the verification for the already added sigs will happen when the next sig is added and if the verification fails the adding itself will fail. So, a callback to the last paragraph, every 106 sigs the naming of `VerifySchnorrSignature()` is actually still correct ;) and the one verify call in the end just verifies the last n % 106 sigs left for that block. I did not put any effort in documenting this properly here yet because the `secp256k1` API is not finalized yet and the details on this particular topic might still change, see for example https://github.com/bitcoin-core/secp256k1/pull/1134#pullrequestreview-1083948472\n\nEverything should be conditional on the presence of a `batch` object which defaults to `nullptr` and if that is the case all the other functions should work as usual, for example when used outside of the context of a new block.\n\n[quoted text omitted]\nYeah, I have been thinking that or maybe even a compiler argument"
  },
  {
   "t": "2024-02-28T13:14:03Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "1825555b6b8d8030e747eda82ddab4b15a87071d"
  },
  {
   "t": "2024-02-28T13:14:09Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "@Sjors maybe what confused you is that `BatchingCachingTransactionSignatureChecker::VerifySchnorrSignatureBatch()` was unused. That was actually a leftover from an earlier design spike and I just forgot to remove it. It's gone now."
  },
  {
   "t": "2024-02-28T13:36:35Z",
   "kind": "comment",
   "who": "Sjors",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nThat clarifies a lot, and should be documented :-)"
  },
  {
   "t": "2024-05-13T08:33:31Z",
   "kind": "comment",
   "who": "0xB10C",
   "assoc": "MEMBER",
   "text": "@willcl-ark and I ran a IBD benchmark vs master for this PR: IBD from a local node to height 840000 and currently only looking at the connect_block duration.\n\nIn the current draft implementation:\n- master is currently faster\n- batch validation is attempted and decreases performance even before taproot activated\n- after taproot activation, batch validation is significantly slower than non-batch validation\n\n![image](https://github.com/bitcoin/bitcoin/assets/19157360/a6fec9ae-eb74-42b0-9861-f777a0dc7f4d)\n\n![image](https://github.com/bitcoin/bitcoin/assets/19157360/b4889d98-ea29-4884-9464-6f41e7e3ac7a)\n\nUsed this bpftrace script to collect CSV data:\n```c\n#!/usr/bin/env bpftrace\n\nusdt:./src/bitcoind:validation:block_connected\n{\n  $height = (int32) arg1;\n  $tx = (uint64) arg2;\n  $ins = (int32) arg3;\n  $sigops = (int64) arg4;\n  $duration = (int64) arg5; // in nanoseconds\n\n  printf(\"%d,%ld,%d,%ld,%ld\\n\", $height, $tx, $ins, $sigops, $duration);\n}\n```"
  },
  {
   "t": "2024-05-13T08:38:04Z",
   "kind": "comment",
   "who": "willcl-ark",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nFrom a quick skim of the changes ISTM that we always use the `BatchingCachingTransactionSignatureChecker`, and there was no switch to the `CachingTransactionSignatureChecker`, but this could well be because this is only in WIP state. It doesnt account for why it's currently slower in all cases though."
  },
  {
   "t": "2024-12-05T13:57:12Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "6f271d7bd0926a926b643f60f65263bed1d07c44"
  },
  {
   "t": "2024-12-07T22:55:54Z",
   "kind": "comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "text": "It seems in this approach the `m_batch_mutex` is held for the entirety of adding, which will cause lock contention. `Verify` is then a blocking call on the main thread, which negates the multithreading.\n\nI think a better approach would be to have a thread local batch for each worker thread and the main thread, so each thread can add all schnorr sigs without locking, and then each thread can verify their batches in parallel at the end."
  },
  {
   "t": "2025-03-11T23:43:41Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "cafdd148f6bd2206da1bf88d684b82feea47f65d"
  },
  {
   "t": "2025-03-16T06:47:20Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/script/sigcache.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/29491/commits/d333d5c6e3c4b454621998772ff74ff6656af691: This is nice! sets us up nicely to later do batch taptweak checking since we can \"collect\" it too."
  },
  {
   "t": "2025-03-17T03:23:19Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/checkqueue.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/29491/commits/454103832351311d11e16505ed1925bd5f1c6875: IIUC All the threads still share this batch. That means we still have the same locking problem; threads will be stuck waiting for the batch to be free.  In my very rough implementation https://github.com/bitcoin-dev-tools/benchcoin/pull/41/commits/173f07dc4493de3d3a9420f6a966606913277e9d#diff-0f68d4063c2fb11000f9048e46478e26c0ea2622f7866d00b574f429c3a4db61 I opted to give each batch a thread to resolve this."
  },
  {
   "t": "2025-03-20T00:17:35Z",
   "kind": "review_comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "path": "src/checkqueue.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": 1997877541,
   "text": "You're right, this wasn't a complete solution. I have improved this now by using buckets of signatures for each thread which are all added and only then verified by the master thread at the end. This could be further improved if we could add the signatures to a batch per thread right away but this would require that we could merge batch objects which the secp api currently doesn't make possible. I'll add that to my wish list because I think it should be even better than the current approach but it should be a comparatively minor improvement. I considered other approaches as well but this seems to be a very simple solution that does not rely on a more complex library or code or uses `thread_local` (which we try to avoid).\n\nYour approach does do batching in each thread but also per each `vChecks` allocation, which are surprisingly small, especially when there are a lot of threads available. This is what caused me to look more into the `nNow` algo behavior. With the current approach here I try to only do one verify call per block which should be the fastest approach once pippenger is used in secp."
  },
  {
   "t": "2025-03-20T00:45:20Z",
   "kind": "review_comment",
   "who": "andrewtoth",
   "assoc": "MEMBER",
   "path": "src/checkqueue.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": 1997877541,
   "text": "I think @Eunovo's approach is closer to what we want. Instead of a bucket per thread, have a batch per thread. However, instead of batch verifying after each `vChecks` batch, batch verify after the queue of checks is empty for the block. We need CCheckQueue::Complete to set a flag that no more checks will be added, and wake all threads again. The threads will all verify their batches once the global queue is empty. We would need to reset the flag after Complete.\n\nThis approach of verifying on master thread at the end will not be faster even with pippinger algo. The max speedup is closer to 2x, but multi threaded is 16x faster."
  },
  {
   "t": "2025-03-20T12:46:37Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/checkqueue.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/29491/commits/6055f3a6f530818e2805d11debb1fb2ce81fc5c0: This leaves the master thread to do all the \"Pubkey recovery\"(I think that's what its called?) work in each `batch.Add` call, which is substantial. We can call \"secp256k1_xonly_pubkey_parse\" when collecting the signatures so the master thread is not stuck doing all the work to parse the XOnlyPubkey."
  },
  {
   "t": "2025-03-23T22:44:42Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "4ab16a0b2801023d5656ec9a0cb9cfdeeeb1a029"
  },
  {
   "t": "2025-03-23T22:59:17Z",
   "kind": "review_comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "path": "src/checkqueue.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": 1997877541,
   "text": "[quoted text omitted]\n\nYes, one batch per thread is the approach I mentioned above as well as preferred, if that wasn't clear. My thinking was that the fastest approach overall would be if we would do that plus efficient merging of batches which should ideally have the same overhead as adding a single signature to a batch (to be verified that this is possible). It would need to be benchmarked across different scenarios for blocks with varying share of schnorr sigs and across different numbers of available threads, of course. I am sure there will be some scenarios where the \"let each thread verify their own batch\" will be faster and there will be other scenarios where \"merge thread batches and verify once\" is faster and we would need to decide based on that in the end. A definite upside of the merging of thread batches would be that it would be simpler code, we could save this round of waking up the threads with the flag set.\n\nHowever, in the meantime I have asked Siv how hard it would be to add the merging of batches to the secp api and it appears it might be harder than I thought because it would require merging of scratch spaces, which is currently not possible. So, while maybe theoretically possible, it's quite far of terms of engineering effort. Siv will still look into it and check if it's feasible at all but until then I will disregard this approach.\n\nSo, long story short, I have now implemented it with a batch per thread using a flag set in complete as you suggested."
  },
  {
   "t": "2025-03-24T13:29:23Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "I've also rebased to get #31689 in here by the way."
  },
  {
   "t": "2025-04-01T20:05:27Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/script/sigcache.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/29491/commits/696bd7547e0e4ab44388d3c55ed636c46f9bdad5: IIUC the original idea was to \"collect the signatures\" and possibly combine them into one batch. Now that a \"one batch per thread\" approach has been taken, should this still be collecting signatures into a vector? the original \"BatchSignatureChecker\" that had a reference to the batch might work better since it skips the vector completely"
  },
  {
   "t": "2025-04-02T10:35:54Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/script/sigcache.h",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": 2023605645,
   "text": "[quoted text omitted]\nOn second thought, it might be better to leave as is because we are still experimenting and trying to find the right number  of batches to use"
  },
  {
   "t": "2025-05-05T16:24:06Z",
   "kind": "comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "text": "While testing this, I observed a decline in performance as the number of threads increased. I'm not sure why this happens, but I suspect these play a role:\n- Doing batch-verification work within the critical section https://github.com/bitcoin/bitcoin/pull/29491/files#diff-0f68d4063c2fb11000f9048e46478e26c0ea2622f7866d00b574f429c3a4db61R151\n- Copy operations in https://github.com/bitcoin/bitcoin/pull/29491/files#diff-612abe4a74f2e9f6903cebff057d47bfe4f8a57507c175743f4b62fde0d3cc58R95-R99\n\nI made some tweaks to my previous batch-verification implementation to produce https://github.com/Eunovo/bitcoin/commits/2025-batch-verification/ and retested. These are results from running the Microbenchmark `build/bin/bench_bitcoin -filter=ConnectBlockAllSchnorr`\n\n| Commit            | Threads | Run 1 (block/s) | Run 2 (block/s) | Run 3 (block/s) | Min   | Max   | Mean (block/s)       | StdDev              |\n|-------------------|---------|------------------|------------------|------------------|--------|--------|------------------------|----------------------|\n| fjahr(4ab16a0b)   | 1       | 8.0              | 8.07             | 7.6              | 7.6    | 8.07   | 7.890000000000001     | 0.20704266871026072  |\n| fjahr(4ab16a0b)   | 2       | 15.54            | 15.74            | 15.66            | 15.54  | 15.74  | 15.646666666666667    | 0.08219218670625349  |\n| fjahr(4ab16a0b)   | 4       | 29.58            | 29.14            | 29.07            | 29.07  | 29.58  | 29.263333333333332    | 0.2257333727111592   |\n| fjahr(4ab16a0b)   | 8       | 49.33            | 45.88            | 48.5             | 45.88  | 49.33  | 47.903333333333336    | 1.4702909764925958   |\n| fjahr(4ab16a0b)   | 16      | 50.75            | 51.23            | 52.17            | 50.75  | 52.17  | 51.383333333333326    | 0.5897645481225736   |\n| master(baa848b8d) | 1       | 6.31             | 6.59             | 6.6              | 6.31   | 6.6    | 6.5                   | 0.13441230102437307  |\n| master(baa848b8d) | 2       | 12.88            | 12.93            | 12.65            | 12.65  | 12.93  | 12.82                 | 0.12192894105447909  |\n| master(baa848b8d) | 4       | 24.96            | 24.96            | 24.93            | 24.93  | 24.96  | 24.95                 | 0.014142135623731487 |\n| master(baa848b8d) | 8       | 46.01            | 46.18            | 46.27            | 46.01  | 46.27  | 46.153333333333336    | 0.10780641085864351  |\n| master(baa848b8d) | 16      | 52.84            | 52.36            | 52.83            | 52.36  | 52.84  | 52.67666666666667     | 0.22395436042987837  |\n| novo(0493ba332)   | 1       | 8.19             | 8.14             | 8.11             | 8.11   | 8.19   | 8.146666666666667     | 0.03299831645537218  |\n| novo(0493ba332)   | 2       | 15.47            | 15.78            | 15.75            | 15.47  | 15.78  | 15.666666666666666    | 0.1396026106091457   |\n| novo(0493ba332)   | 4       | 30.23            | 30.46            | 30.28            | 30.23  | 30.46  | 30.323333333333334    | 0.09877021593352711  |\n| novo(0493ba332)   | 8       | 53.96            | 50.33            | 54.68            | 50.33  | 54.68  | 52.99                 | 1.9037331745809347   |\n| novo(0493ba332)   | 16      | 64.4             | 64.59            | 64.29            | 64.29  | 64.59  | 64.42666666666668     | 0.1239175353029395   |\n\nHere's a graph of the mean block processing speed (block/s) plotted against the number of threads (Higher is better).\n![benchmark_comparison](https://github.com/user-attachments/assets/0b328e10-53b4-4689-a4ca-3c0fb561354d)\n\n**EDIT**\nI'm waiting on more results from a fresh IBD benchmark"
  },
  {
   "t": "2025-05-05T17:10:54Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nCan you share the setup for these benchmarks so I can test them? Thanks!\n\nEDIT: I see you added it, it wasn't in the notification I received"
  },
  {
   "t": "2025-05-06T06:40:38Z",
   "kind": "comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nOS is Ubuntu 24.04.2 LTS\n\nOutput from `lspcu`\n```\nArchitecture:             x86_64\n  CPU op-mode(s):         32-bit, 64-bit\n  Address sizes:          48 bits physical, 48 bits virtual\n  Byte Order:             Little Endian\nCPU(s):                   16\n  On-line CPU(s) list:    0-15\nVendor ID:                AuthenticAMD\n  Model name:             AMD Ryzen 9 8945HS w/ Radeon 780M Graphics\n    CPU family:           25\n    Model:                117\n    Thread(s) per core:   2\n    Core(s) per socket:   8\n    Socket(s):            1\n    Stepping:             2\n    Frequency boost:      enabled\n    CPU(s) scaling MHz:   83%\n    CPU max MHz:          5263.0000\n    CPU min MHz:          400.0000\n```\n\nI'm running the IBD benchmark on a different machine. I'll share details when results are in.\n\n**EDIT**\nIt seems you were referring to the command I ran. I used `ConnectBlockAllSchnorr` microbenchmark. Run it with `build/bin/bench_bitcoin -filter=ConnectBlockAllSchnorr` . For IBD benchmarking, I'm doing `bitcoind -assumevalid=0 -par={par} -stopatheight=880000`(I'm testing 1, 2, 4, 6,  8, 12 threads) with [Benchkit](https://github.com/bitcoin-dev-tools/benchkit/tree/master).\n\nNote that Benchkit is still experimental, it loads the 840000 assumeutxo snapshot and runs hyperfine"
  },
  {
   "t": "2025-05-18T23:57:21Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "4d9834e80cfd95795e9cca53a4cff75d22c7f3ad"
  },
  {
   "t": "2025-05-18T23:58:49Z",
   "kind": "comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI couldn't figure out how to test the different thread configurations for the microbench with Benchkit. Are you using Benchkit there as well or are you just setting `worker_threads_num` in `test/util/setup_common.cpp`? That's what I have been doing for now.\n\n[quoted text omitted]\nI am not sure how to do this completely without copy, using span is unsafe because vChecks is cleared. Using emplace_back is possible and I am using it now but in the benchmarks I didn\u2019t see noticable improvement from this. Can you be a bit more specific what you had in mind?\n\n[quoted text omitted]\nI have implemented this and see a performance boost from it for the case with 15 worker threads (I didn't test all the other thread configuration yet). On my machine I now get these results (running `build/bin/bench_bitcoin -filter=ConnectBlockAllSchnorr -min-time=1000`):\n\nnovo (15 worker threads)\n```\n|            ns/block |             block/s |    err% |     total | benchmark\n|--------------------:|--------------------:|--------:|----------:|:----------\n|        9,285,356.00 |              107.70 |    1.6% |      1.09 | `ConnectBlockAllSchnorr`\n```\n\nthis latest state 9d460e82e1404f7cfb29613d3852bbb99e0071ad (15 worker threads)\n```\n|            ns/block |             block/s |    err% |     total | benchmark\n|--------------------:|--------------------:|--------:|----------:|:----------\n|        7,973,364.58 |              125.42 |    1.3% |      1.10 | `ConnectBlockAllSchnorr`\n```\n\nThank you for the continued valuable feedback, I have made you co-author of the relevant commit.\n\nA few comments on the latest code in the branch you linked to (0493ba332aba6ff0d6da55094fd8d834bb8cc18f):\n- Tracking if anything has been added to the batch and skipping verify makes sense as an idea but I have some issues with it: First of all, using the return value of `secp256k1_batch_add_schnorrsig` seems wrong because this will return false if something has gone wrong in adding the signature and this will usually mean that we have a problem, at least in the block verification process it definitely means that. So taking that to mean we don\u2019t have to verify anymore, it\u2019s fine doesn\u2019t seem right. Additionally in my opinion we shouldn\u2019t have to track this in bitcoin core because I believe secp256k1 should handle this. Such behavior should happen closest to the actual data IMO and if this is actually the case calling verify should be costless. In the current secp code the actual verification steps seem to be skipped here: https://github.com/bitcoin-core/secp256k1/pull/1134/files#diff-5a633b880f8591184912dc3e49b3dffc2af714b2fc27404d0a1b12f6a2df7a71R191 so it would be surprising to me if adding `m_needs_verify` gives a real performance boost. Did you benchmark this change in isolation and notice a performance impact? If you did see that I would be interested to dig into why that is. And fwiw, I could also be pursuaded to not trust secp with this and track this ourselves if this is what we usually do in core but I will need to look for some comparable reference code and understand the reasoning for this (seemingly) duplicate behavior.\n- IIUC you are using `queue.empty()` as the indicator that all the todos have been added, if I remember correctly I tried this at some point too but it turned out that there is no guarantee for that and it could be that the todos are just not added fast enough, hence the need for `m_complete`. So this means some threads could be verifying multiple times within a block. I don\u2019t see why this would be causing problems with the verification though, I think the result is still correct in this regard. But the question is how this impacts performance. Here as well I would find it kind of surprising if this is faster in practice but maybe my thinking is wrong and your benchmarks tell us we should switch to this approach instead, letting threads verify their batch when they have the chance instead of letting them go to sleep. But I also think we need to wait until we have pippenger as well because that could influence these results significantly I think.\n- Running the loop one more time only with `nNow = 0` to trigger the batch verification is very simple but I don\u2019t see how you ensure that all the thread batches have been verified before the master thread returns. It looks to me like as soon as the master thread has verified its batch it simply returns, no matter how many (or any) other threads have verified their batches already and any failures in these batches would be missed. This is what I track with `m_total_verified` here in the code. Additionally, setting `nNow = 0` has the unwanted side-effect that in the cleanup phase of the next iteration the loop seems to be in the very first iteration again and this leads to running `nTotal++;` giving the impression that there are more threads available. I guess this doesn\u2019t cause issues only because there is no tracking of how many of the total threads have already verified their batch.\n\nA few more comments on this latest push:\n- I have also been warming up to the idea of having the thread local batch object in `Loop` rather than in a checkqueue member, this saves us a few lines of code and I didn't see an impact on performance.\n- Additionally I have been looking into some c++20 multithreading features since the latest push and tested using std::barrier instead of the counter but so far I didn't see an improvement in performance or code clarity from it.\n- I have added a unit test for `BatchSchnorrVerifier`.\n- Just FYI, I don't think rebasing currently makes sense for me here because this would require rebasing the secp batch branch and the author is working on doing that shortly themselves so I would rather wait for that."
  },
  {
   "t": "2025-06-12T06:20:40Z",
   "kind": "comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nSorry I didn't state that. That's what I did for the microbench. I only use Benchkit for IBD bechmarks.\n\nI'm going to review and test latest changes."
  },
  {
   "t": "2025-06-12T08:22:29Z",
   "kind": "comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "text": "[quoted text omitted]\n\nI was thinking it might be better to move away from the collecting sigs idea if the copy operations present a significant slowdown. I'll see if I can detect any performance changes in testing.\n\n[quoted text omitted]\nMakes sense. I agree. We do not need to do this in core.\n\n[quoted text omitted]\nIt seems like the major reason for performance drop was the batch-verification work being performed in the critical section. I'll try to reproduce your latest results."
  },
  {
   "t": "2025-06-15T19:32:11Z",
   "kind": "review_comment",
   "who": "Eunovo",
   "assoc": "MEMBER",
   "path": "src/batchverify.cpp",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": null,
   "text": "https://github.com/bitcoin/bitcoin/pull/29491/commits/bcd51a49f8b8892254288d7522435287f055f92f: why discard the return value of `secp256k1_xonly_pubkey_parse()`?"
  },
  {
   "t": "2025-07-24T15:30:21Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6"
  },
  {
   "t": "2026-01-26T12:49:56Z",
   "kind": "review_comment",
   "who": "fjahr",
   "assoc": "MEMBER",
   "path": "src/batchverify.cpp",
   "commit": "bcbbb574b17a0449fa38f65f7c7f5a4f01a825b6",
   "in_reply_to": 2148806441,
   "text": "Not discarding it anymore in the latest push."
  },
  {
   "t": "2026-01-26T15:56:25Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "452a3754bf77adb7484d789469973f05d4c0e692"
  },
  {
   "t": "2026-01-26T15:57:38Z",
   "kind": "review",
   "who": "fjahr",
   "assoc": "MEMBER",
   "state": "COMMENTED",
   "commit": "452a3754bf77adb7484d789469973f05d4c0e692",
   "text": "Rebased on latest master and included the latest API changes in the batch module PR in secp256k1."
  },
  {
   "t": "2026-01-27T12:53:33Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "3029493ef1e1a07995304b08001225f9fda6e882"
  },
  {
   "t": "2026-01-27T16:19:15Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "c5a124f429d7447bf08d2d82a8af5b294acfa759"
  },
  {
   "t": "2026-01-27T17:00:25Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "3979539ced5afc82a7858538ca9c49a5cd16cc43"
  },
  {
   "t": "2026-01-29T15:42:08Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "46d1cf7efe2019e379bf2ebc1e4f59701d4d111e"
  },
  {
   "t": "2026-01-29T16:19:23Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "41e0ea662abdc5259ee05080f4e0a037a38e5d4c"
  },
  {
   "t": "2026-01-31T14:00:06Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "f178c6cba7d179308324ff4a21b7ff20fc09ec2c"
  },
  {
   "t": "2026-07-31T15:34:01Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "310df214a427821918b0e34951fea250213fd338"
  },
  {
   "t": "2026-08-01T08:32:29Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "572960d8804421b8656fcdb2b989acc408f342e8"
  },
  {
   "t": "2026-08-01T09:18:01Z",
   "kind": "force_push",
   "who": "fjahr",
   "commit": "c4c9aa200d341377fe528de1e30d04fe370c1e09"
  }
 ],
 "labels_log": [
  {
   "t": "2024-02-27T17:10:59Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2024-04-06T10:25:59Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2024-12-05T15:35:46Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-20T08:03:08Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-03-23T23:11:20Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-05-06T23:29:39Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-24T17:20:16Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-07-31T22:54:19Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2025-09-26T12:00:23Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "maflcko"
  },
  {
   "t": "2026-01-26T16:54:51Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-26T18:01:44Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-29T17:51:32Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-31T01:08:21Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-01-31T15:13:49Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-02-03T13:29:30Z",
   "action": "labeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-31T15:58:15Z",
   "action": "unlabeled",
   "label": "Needs rebase",
   "who": "DrahtBot"
  },
  {
   "t": "2026-07-31T17:06:23Z",
   "action": "labeled",
   "label": "CI failed",
   "who": "DrahtBot"
  },
  {
   "t": "2026-08-01T10:20:51Z",
   "action": "unlabeled",
   "label": "CI failed",
   "who": "DrahtBot"
  }
 ],
 "state_log": [
  {
   "t": "2025-03-11T23:44:24Z",
   "kind": "renamed",
   "who": "fjahr",
   "from": "[DO NOT MERGE] Schnorr batch verification for blocks",
   "to": "[EXPERIMENTAL] Schnorr batch verification for blocks"
  }
 ],
 "text_chars": 25268,
 "text_tokens_estimate": 6317,
 "changed_paths": [
  "src/CMakeLists.txt",
  "src/batchverify.cpp",
  "src/batchverify.h",
  "src/checkqueue.h",
  "src/kernel/CMakeLists.txt",
  "src/script/script_error.cpp",
  "src/script/script_error.h",
  "src/script/sigcache.cpp",
  "src/script/sigcache.h",
  "src/secp256k1/.github/workflows/ci.yml",
  "src/secp256k1/.gitignore",
  "src/secp256k1/CMakeLists.txt",
  "src/secp256k1/Makefile.am",
  "src/secp256k1/README.md",
  "src/secp256k1/ci/ci.sh",
  "src/secp256k1/configure.ac",
  "src/secp256k1/doc/speedup-batch.md",
  "src/secp256k1/doc/speedup-batch/.gitignore",
  "src/secp256k1/doc/speedup-batch/Makefile",
  "src/secp256k1/doc/speedup-batch/bench.sh",
  "src/secp256k1/doc/speedup-batch/bench_output.txt",
  "src/secp256k1/doc/speedup-batch/plot.gp",
  "src/secp256k1/doc/speedup-batch/schnorrsig-speedup-batch.png",
  "src/secp256k1/doc/speedup-batch/tweakcheck-speedup-batch.png",
  "src/secp256k1/examples/CMakeLists.txt",
  "src/secp256k1/examples/batch.c",
  "src/secp256k1/include/secp256k1_batch.h",
  "src/secp256k1/include/secp256k1_schnorrsig_batch.h",
  "src/secp256k1/include/secp256k1_tweak_check_batch.h",
  "src/secp256k1/src/CMakeLists.txt",
  "src/secp256k1/src/bench.c",
  "src/secp256k1/src/ecmult_impl.h",
  "src/secp256k1/src/modules/batch/Makefile.am.include",
  "src/secp256k1/src/modules/batch/main_impl.h",
  "src/secp256k1/src/modules/batch/tests_impl.h",
  "src/secp256k1/src/modules/extrakeys/Makefile.am.include",
  "src/secp256k1/src/modules/extrakeys/batch_add_impl.h",
  "src/secp256k1/src/modules/extrakeys/batch_add_tests_impl.h",
  "src/secp256k1/src/modules/extrakeys/bench_impl.h",
  "src/secp256k1/src/modules/schnorrsig/Makefile.am.include",
  "src/secp256k1/src/modules/schnorrsig/batch_add_impl.h",
  "src/secp256k1/src/modules/schnorrsig/batch_add_tests_impl.h",
  "src/secp256k1/src/modules/schnorrsig/bench_impl.h",
  "src/secp256k1/src/modules/schnorrsig/tests_impl.h",
  "src/secp256k1/src/secp256k1.c",
  "src/secp256k1/src/tests.c",
  "src/test/fuzz/CMakeLists.txt",
  "src/test/fuzz/batchverify.cpp",
  "src/test/fuzz/checkqueue.cpp",
  "src/test/key_tests.cpp",
  "src/test/txvalidationcache_tests.cpp",
  "src/validation.cpp",
  "src/validation.h",
  "test/functional/feature_taproot.py",
  "test/functional/feature_taproot_invalid_output_key.py",
  "test/functional/test_runner.py"
 ],
 "files": [
  {
   "path": "src/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/batchverify.cpp",
   "add": 48,
   "del": 0
  },
  {
   "path": "src/batchverify.h",
   "add": 49,
   "del": 0
  },
  {
   "path": "src/checkqueue.h",
   "add": 125,
   "del": 29
  },
  {
   "path": "src/kernel/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/script/script_error.cpp",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/script/script_error.h",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/script/sigcache.cpp",
   "add": 22,
   "del": 0
  },
  {
   "path": "src/script/sigcache.h",
   "add": 20,
   "del": 0
  },
  {
   "path": "src/secp256k1/.github/workflows/ci.yml",
   "add": 25,
   "del": 13
  },
  {
   "path": "src/secp256k1/.gitignore",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/secp256k1/CMakeLists.txt",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/secp256k1/Makefile.am",
   "add": 15,
   "del": 0
  },
  {
   "path": "src/secp256k1/README.md",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/secp256k1/ci/ci.sh",
   "add": 2,
   "del": 1
  },
  {
   "path": "src/secp256k1/configure.ac",
   "add": 10,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch.md",
   "add": 17,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/.gitignore",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/Makefile",
   "add": 27,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/bench.sh",
   "add": 13,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/bench_output.txt",
   "add": 119,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/plot.gp",
   "add": 41,
   "del": 0
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/schnorrsig-speedup-batch.png",
   "add": null,
   "del": null
  },
  {
   "path": "src/secp256k1/doc/speedup-batch/tweakcheck-speedup-batch.png",
   "add": null,
   "del": null
  },
  {
   "path": "src/secp256k1/examples/CMakeLists.txt",
   "add": 4,
   "del": 0
  },
  {
   "path": "src/secp256k1/examples/batch.c",
   "add": 152,
   "del": 0
  },
  {
   "path": "src/secp256k1/include/secp256k1_batch.h",
   "add": 88,
   "del": 0
  },
  {
   "path": "src/secp256k1/include/secp256k1_schnorrsig_batch.h",
   "add": 39,
   "del": 0
  },
  {
   "path": "src/secp256k1/include/secp256k1_tweak_check_batch.h",
   "add": 45,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/CMakeLists.txt",
   "add": 11,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/bench.c",
   "add": 37,
   "del": 2
  },
  {
   "path": "src/secp256k1/src/ecmult_impl.h",
   "add": 54,
   "del": 19
  },
  {
   "path": "src/secp256k1/src/modules/batch/Makefile.am.include",
   "add": 3,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/batch/main_impl.h",
   "add": 202,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/batch/tests_impl.h",
   "add": 126,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/extrakeys/Makefile.am.include",
   "add": 8,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/extrakeys/batch_add_impl.h",
   "add": 147,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/extrakeys/batch_add_tests_impl.h",
   "add": 134,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/extrakeys/bench_impl.h",
   "add": 138,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/schnorrsig/Makefile.am.include",
   "add": 7,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/schnorrsig/batch_add_impl.h",
   "add": 154,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/schnorrsig/batch_add_tests_impl.h",
   "add": 245,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/schnorrsig/bench_impl.h",
   "add": 46,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/modules/schnorrsig/tests_impl.h",
   "add": 47,
   "del": 3
  },
  {
   "path": "src/secp256k1/src/secp256k1.c",
   "add": 10,
   "del": 0
  },
  {
   "path": "src/secp256k1/src/tests.c",
   "add": 137,
   "del": 35
  },
  {
   "path": "src/test/fuzz/CMakeLists.txt",
   "add": 1,
   "del": 0
  },
  {
   "path": "src/test/fuzz/batchverify.cpp",
   "add": 52,
   "del": 0
  },
  {
   "path": "src/test/fuzz/checkqueue.cpp",
   "add": 2,
   "del": 0
  },
  {
   "path": "src/test/key_tests.cpp",
   "add": 85,
   "del": 18
  },
  {
   "path": "src/test/txvalidationcache_tests.cpp",
   "add": 15,
   "del": 14
  },
  {
   "path": "src/validation.cpp",
   "add": 52,
   "del": 8
  },
  {
   "path": "src/validation.h",
   "add": 21,
   "del": 3
  },
  {
   "path": "test/functional/feature_taproot.py",
   "add": 38,
   "del": 37
  },
  {
   "path": "test/functional/feature_taproot_invalid_output_key.py",
   "add": 93,
   "del": 0
  },
  {
   "path": "test/functional/test_runner.py",
   "add": 1,
   "del": 0
  }
 ],
 "test_lines": 356,
 "git": {
  "head": "c4c9aa200d341377fe528de1e30d04fe370c1e09",
  "head_matches_backup": true,
  "base": "67efced1fc83a0b7215cc1513e7c4754fee0f12f",
  "commits": [
   {
    "sha": "6885e17bab",
    "subject": "Squashed 'src/secp256k1/' changes from d2d04864ef9..422a1d5c5f5",
    "files": 37,
    "add": 2108,
    "del": 73
   },
   {
    "sha": "07bcd03eed",
    "subject": "Update secp256k1 subtree to include batch verification module",
    "files": 37,
    "add": 2108,
    "del": 73
   },
   {
    "sha": "4ce833a75b",
    "subject": "validation: Add BatchVerify (unused)",
    "files": 4,
    "add": 99,
    "del": 0
   },
   {
    "sha": "174c68dc46",
    "subject": "script: Add batch validation error",
    "files": 2,
    "add": 5,
    "del": 0
   },
   {
    "sha": "41580d4798",
    "subject": "sigcache: Add CollectingSignatureChecker",
    "files": 2,
    "add": 43,
    "del": 0
   },
   {
    "sha": "54bb0b4d4b",
    "subject": "validation: Add BatchableScriptChecker",
    "files": 2,
    "add": 36,
    "del": 1
   },
   {
    "sha": "cbb03d5ec0",
    "subject": "validation: Use schnorr signature batch validation in parallel script verification",
    "files": 7,
    "add": 180,
    "del": 81
   },
   {
    "sha": "0c33117a56",
    "subject": "validation: Use schnorr batch validation in single thread case",
    "files": 2,
    "add": 41,
    "del": 14
   },
   {
    "sha": "71f8b7b262",
    "subject": "fuzz: Add basic fuzz test for BatchSchnorrVerifier",
    "files": 2,
    "add": 53,
    "del": 0
   },
   {
    "sha": "61163de9d1",
    "subject": "test: Add BatchSchnorrVerifier unit test",
    "files": 1,
    "add": 85,
    "del": 18
   },
   {
    "sha": "c4c9aa200d",
    "subject": "test: Add functional test for unspendable taproot output key",
    "files": 2,
    "add": 94,
    "del": 0
   }
  ],
  "patch_truncated": true
 },
 "input_hash": "c33a72850bec43aa",
 "extracted_at": "2026-09-17T16:15:31+00:00"
}