Repository: bitcoin-core/secp256k1
Pull request #1049 (2021-12-23 01:13:39)

Faster fixed-input ecmult tests
Given how much #920 slowed down the tests with low iteration count, replace it with 3 different similar test:
* count >= 1: a test with 1024 multiplies that tests any pattern of 6 bits in windows not more than 20 bits wide
* count >= 3: a test with 2048 multiplies that tests any pattern of 8 consecutive bits
* count >= 35: the old test (which effectively tests all 2-bit patterns)


real-or-random: Is this the first test where we run essentially different checks depending on the count variable?

I'm slightly concerned that it introduces an "unexpected" element in the tests.
real-or-random: > @real-or-random I see your concern. The way I'm thinking about it is, "this test does a bunch of ecmults and checks the result; `count` just controls how many ecmults are being done".

Indeed but your numbers show that it's really significant on valgrind... Maybe for the paranoid, we should document a number where it's guaranteed that all tests run? I guess anyway all tests should run at 64 but maybe even lower? For example, is there a reason why you choose 35? (And not 32? Sorry, I don't want to start bikeshedding over the number, just trying to understand.)

sipa: This is indeed somewhat unusual, at least compared to what we had before, where the code paths used never depend on the count variable.

My justification here is that while the count variable now changes the _test_ code paths, it doesn't (or at least, shouldn't) change the code paths _being tested_. Increasing count just runs it with more (fixed) inputs.

The reasoning is something like: #920 was probably overkill, but I also don't want to throw it away for cases where we have enough time to still run it. So replace it with a significantly cheaper test that tests the same code paths (and mostly the same table entries; possibly even more), but also still run the old code if count is high enough.

If this feels all too arbitrary (which I find understandable), I'm also fine with just replacing the whole thing with only the consecutive-8-bits 2048-point test.

@robot-dreams

```python
patterns = []
for i in range(1, 1<<20, 2):
    if bin(i).count("1") == 6:
        for j in range(256):
            if (i << j) >> 256 == 0:
                patterns.append(i << j)
```

then for each `pattern` in `patterns`, it should should hold that `x & pattern` takes on 64 different values as `x` iterates over the scalars in the test.
real-or-random: Concept ACK

> The reasoning is something like: #920 was probably overkill, but I also don't want to throw it away for cases where we have enough time to still run it. 

This was also my initial intuition and I agree. It's not elegant but it's better than throwing tests away.  This will also be helpful for other "overkill" tests, e.g., see the discussion about an "million bytes" SHA256 test: https://github.com/bitcoin-core/secp256k1/pull/731#discussion_r413326188

Maybe this is the time to add a `-h` / `--help` and then mention that not all tests may be run when the count is lower than the default. 
sipa: > For example, is there a reason why you choose 35? (And not 32)?

Specifying count=1 does ~1024 multiplications, count=3 does ~1024*3, count=35 does ~1024*35.

Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index ffbd820..35a9a45 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -4,10 +4,10 @@ env:
   # Specific warnings can be disabled with -Wno-error=foo.
   # -pedantic-errors is not equivalent to -Werror=pedantic and thus not implied by -Werror according to the GCC manual.
   WERROR_CFLAGS: -Werror -pedantic-errors
-  MAKEFLAGS: -j4
+  MAKEFLAGS: -j2
   BUILD: check
   ### secp256k1 config
-  ECMULTWINDOW: auto
+  STATICPRECOMPUTATION: yes
   ECMULTGENPRECISION: auto
   ASM: no
   WIDEMUL: auto
@@ -50,19 +50,14 @@ merge_base_script_snippet: &MERGE_BASE
     - git config --global user.name "ci"
     - git merge FETCH_HEAD  # Merge base to detect silent merge conflicts
 
-linux_container_snippet: &LINUX_CONTAINER
+task:
+  name: "x86_64: Linux (Debian stable)"
   container:
     dockerfile: ci/linux-debian.Dockerfile
     # Reduce number of CPUs to be able to do more builds in parallel.
     cpu: 1
-    # Gives us more CPUs for free if they're available.
-    greedy: true
     # More than enough for our scripts.
     memory: 1G
-
-task:
-  name: "x86_64: Linux (Debian stable)"
-  << : *LINUX_CONTAINER
   matrix: &ENV_MATRIX
     - env: {WIDEMUL:  int64,  RECOVERY: yes}
     - env: {WIDEMUL:  int64,                 ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
@@ -71,11 +66,12 @@ task:
     - env: {WIDEMUL: int128,                 ECDH: yes, EXPERIMENTAL: yes, SCHNORRSIG: yes}
     - env: {WIDEMUL: int128,  ASM: x86_64}
     - env: {                  RECOVERY: yes,            EXPERIMENTAL: yes, SCHNORRSIG: yes}
+    - env: {                  STATICPRECOMPUTATION: no}
     - env: {BUILD: distcheck, WITH_VALGRIND: no, CTIMETEST: no, BENCH: no}
     - env: {CPPFLAGS: -DDETERMINISTIC}
     - env: {CFLAGS: -O0, CTIMETEST: no}
-    - env: { ECMULTGENPRECISION: 2, ECMULTWINDOW: 2 }
-    - env: { ECMULTGENPRECISION: 8, ECMULTWINDOW: 4 }
+    - env: { ECMULTGENPRECISION: 2 }
+    - env: { ECMULTGENPRECISION: 8 }
   matrix:
     - env:
         CC: gcc
@@ -88,7 +84,10 @@ task:
 
 task:
   name: "i686: Linux (Debian stable)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     HOST: i686-linux-gnu
     ECDH: yes
@@ -135,9 +134,8 @@ task:
   ##   - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
   ##
   brew_valgrind_pre_script:
-    - brew update
     - brew config
-    - brew tap LouisBrunner/valgrind
+    - brew tap --shallow LouisBrunner/valgrind
     # Fetch valgrind source but don't build it yet.
     - brew fetch --HEAD LouisBrunner/valgrind/valgrind
   brew_valgrind_cache:
@@ -167,7 +165,10 @@ task:
 
 task:
   name: "s390x (big-endian): Linux (Debian stable, QEMU)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     WRAPPER_CMD: qemu-s390x
     SECP256K1_TEST_ITERS: 16
@@ -187,7 +188,10 @@ task:
 
 task:
   name: "ARM32: Linux (Debian stable, QEMU)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     WRAPPER_CMD: qemu-arm
     SECP256K1_TEST_ITERS: 16
@@ -208,7 +212,10 @@ task:
 
 task:
   name: "ARM64: Linux (Debian stable, QEMU)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     WRAPPER_CMD: qemu-aarch64
     SECP256K1_TEST_ITERS: 16
@@ -226,7 +233,10 @@ task:
 
 task:
   name: "ppc64le: Linux (Debian stable, QEMU)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     WRAPPER_CMD: qemu-ppc64le
     SECP256K1_TEST_ITERS: 16
@@ -244,7 +254,10 @@ task:
 
 task:
   name: "x86_64 (mingw32-w64): Windows (Debian stable, Wine)"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     WRAPPER_CMD: wine64-stable
     SECP256K1_TEST_ITERS: 16
@@ -262,7 +275,10 @@ task:
 
 # Sanitizers
 task:
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 2G
   env:
     ECDH: yes
     RECOVERY: yes
@@ -271,15 +287,11 @@ task:
     CTIMETEST: no
   matrix:
     - name: "Valgrind (memcheck)"
-      container:
-        cpu: 2
       env:
         # The `--error-exitcode` is required to make the test fail if valgrind found errors, otherwise it'll return 0 (https://www.valgrind.org/docs/manual/manual-core.html)
         WRAPPER_CMD: "valgrind --error-exitcode=42"
         SECP256K1_TEST_ITERS: 2
     - name: "UBSan, ASan, LSan"
-      container:
-        memory: 2G
       env:
         CFLAGS: "-fsanitize=undefined,address -g"
         UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1"
@@ -290,10 +302,11 @@ task:
   matrix:
     - env:
         ASM: auto
+        STATICPRECOMPUTATION: yes
     - env:
         ASM: no
+        STATICPRECOMPUTATION: no
         ECMULTGENPRECISION: 2
-        ECMULTWINDOW: 2
   matrix:
     - env:
         CC: clang
@@ -307,12 +320,15 @@ task:
 
 task:
   name: "C++ -fpermissive"
-  << : *LINUX_CONTAINER
+  container:
+    dockerfile: ci/linux-debian.Dockerfile
+    cpu: 1
+    memory: 1G
   env:
     # ./configure correctly errors out when given CC=g++.
     # We hack around this by passing CC=g++ only to make.
     CC: gcc
-    MAKEFLAGS: -j4 CC=g++ CFLAGS=-fpermissive\ -g
+    MAKEFLAGS: -j2 CC=g++ CFLAGS=-fpermissive\ -g
     WERROR_CFLAGS:
     EXPERIMENTAL: yes
     ECDH: yes
diff --git a/Makefile.am b/Makefile.am
index f878529..11072bc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,7 +88,7 @@ endif
 libsecp256k1_la_SOURCES = src/secp256k1.c
 libsecp256k1_la_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
 libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB) $(PRECOMPUTED_LIB)
-libsecp256k1_la_LDFLAGS = -no-undefined -version-info $(LIB_VERSION_CURRENT):$(LIB_VERSION_REVISION):$(LIB_VERSION_AGE)
+libsecp256k1_la_LDFLAGS = -no-undefined
 
 if VALGRIND_ENABLED
 libsecp256k1_la_CPPFLAGS += -DVALGRIND
diff --git a/ci/cirrus.sh b/ci/cirrus.sh
index 4379e25..e27b347 100755
--- a/ci/cirrus.sh
+++ b/ci/cirrus.sh
@@ -15,8 +15,7 @@ valgrind --version || true
 ./configure \
     --enable-experimental="$EXPERIMENTAL" \
     --with-test-override-wide-multiply="$WIDEMUL" --with-asm="$ASM" \
-    --with-ecmult-window="$ECMULTWINDOW" \
-    --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
+    --enable-ecmult-static-precomputation="$STATICPRECOMPUTATION" --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
     --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
     --enable-module-schnorrsig="$SCHNORRSIG" \
     --with-valgrind="$WITH_VALGRIND" \
diff --git a/configure.ac b/configure.ac
index 92e497d..f7e9e3d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,24 +1,5 @@
 AC_PREREQ([2.60])
-
-# The package (a.k.a. release) version is based on semantic versioning 2.0.0 of
-# the API. All changes in experimental modules are treated as
-# backwards-compatible and therefore at most increase the minor version.
-define(_PKG_VERSION_MAJOR, 0)
-define(_PKG_VERSION_MINOR, 1)
-define(_PKG_VERSION_BUILD, 0)
-define(_PKG_VERSION_IS_RELEASE, false)
-
-# The library version is based on libtool versioning of the ABI. The set of
-# rules for updating the version can be found here:
-# https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html
-# All changes in experimental modules are treated as if they don't affect the
-# interface and therefore only increase the revision.
-define(_LIB_VERSION_CURRENT, 0)
-define(_LIB_VERSION_REVISION, 0)
-define(_LIB_VERSION_AGE, 0)
-
-AC_INIT([libsecp256k1],m4_join([.], _PKG_VERSION_MAJOR, _PKG_VERSION_MINOR, _PKG_VERSION_BUILD)m4_if(_PKG_VERSION_IS_RELEASE, [true], [], [-pre]),[https://github.com/bitcoin-core/secp256k1/issues],[libsecp256k1],[https://github.com/bitcoin-core/secp256k1])
-
+AC_INIT([libsecp256k1],[0.1])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_MACRO_DIR([build-aux/m4])
 AC_CANONICAL_HOST
@@ -400,9 +381,6 @@ AM_CONDITIONAL([ENABLE_MODULE_EXTRAKEYS], [test x"$enable_module_extrakeys" = x"
 AM_CONDITIONAL([ENABLE_MODULE_SCHNORRSIG], [test x"$enable_module_schnorrsig" = x"yes"])
 AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"])
 AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"])
-AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT)
-AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION)
-AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE)
 
 # Make sure nothing new is exported so that we don't break the cache.
 PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH"
diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md
deleted file mode 100644
index 3c4c2e4..0000000
--- a/doc/CHANGELOG.md
+++ /dev/null
@@ -1,12 +0,0 @@
-# Changelog
-
-This file is currently only a template for future use.
-
-Each change falls into one of the following categories: Added, Changed, Deprecated, Removed, Fixed or Security.
-
-## [Unreleased]
-
-## [MAJOR.MINOR.PATCH] - YYYY-MM-DD
-
-### Added/Changed/Deprecated/Removed/Fixed/Security
-- [Title with link to Pull Request](https://link-to-pr)
diff --git a/doc/release-process.md b/doc/release-process.md
deleted file mode 100644
index a35b8a9..0000000
--- a/doc/release-process.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Release Process
-
-1. Open PR to master that
-   1. adds release notes to `doc/CHANGELOG.md` and
-   2. if this is **not** a patch release, updates `_PKG_VERSION_{MAJOR,MINOR}` and `_LIB_VERSIONS_*` in `configure.ac`
-2. After the PR is merged,
-   * if this is **not** a patch release, create a release branch with name `MAJOR.MINOR`.
-     Make sure that the branch contains the right commits.
-     Create commit on the release branch that sets `_PKG_VERSION_IS_RELEASE` in `configure.ac` to `true`.
-   * if this **is** a patch release, open a pull request with the bugfixes to the `MAJOR.MINOR` branch.
-     Also include the release note commit bump `_PKG_VERSION_BUILD` and `_LIB_VERSIONS_*` in `configure.ac`.
-4. Tag the commit with `git tag -s vMAJOR.MINOR.PATCH`.
-5. Push branch and tag with `git push origin --tags`.
-6. Create a new GitHub release with a link to the corresponding entry in `doc/CHANGELOG.md`.
diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h
index 3d198dc..30b151f 100644
--- a/src/ecmult_const_impl.h
+++ b/src/ecmult_const_impl.h
@@ -12,22 +12,6 @@
 #include "ecmult_const.h"
 #include "ecmult_impl.h"
 
-/** Fill a table 'pre' with precomputed odd multiples of a.
- *
- *  The resulting point set is brought to a single constant Z denominator, stores the X and Y
- *  coordinates as ge_storage points in pre, and stores the global Z in globalz.
- *  It only operates on tables sized for WINDOW_A wnaf multiples.
- */
-static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) {
-    secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)];
-    secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
-
-    /* Compute the odd multiples in Jacobian form. */
-    secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a);
-    /* Bring them to the same Z denominator. */
-    secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
-}
-
 /* This is like `ECMULT_TABLE_GET_GE` but is constant time */
 #define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \
     int m = 0; \
@@ -56,6 +40,7 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
     secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \
 } while(0)
 
+
 /** Convert a number to WNAF notation.
  *  The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val.
  *  It has the following guarantees:
@@ -71,7 +56,7 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
  */
 static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w, int size) {
     int global_sign;
-    int skew;
+    int skew = 0;
     int word = 0;
 
     /* 1 2 3 */
@@ -79,7 +64,9 @@ static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w
     int u;
 
     int flip;
-    secp256k1_scalar s = *scalar;
+    int bit;
+    secp256k1_scalar s;
+    int not_neg_one;
 
     VERIFY_CHECK(w > 0);
     VERIFY_CHECK(size > 0);
@@ -87,19 +74,33 @@ static int secp256k1_wnaf_const(int *wnaf, const secp256k1_scalar *scalar, int w
     /* Note that we cannot handle even numbers by negating them to be odd, as is
      * done in other implementations, since if our scalars were specified to have
      * width < 256 for performance reasons, their negations would have width 256
-     * and we'd lose any performance benefit. Instead, we use a variation of a
-     * technique from Section 4.2 of the Okeya/Tagaki paper, which is to add 1 to the
-     * number we are encoding when it is even, returning a skew value indicating
+     * and we'd lose any performance benefit. Instead, we use a technique from
+     * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even)
+     * or 2 (for odd) to the number we are encoding, returning a skew value indicating
      * this, and having the caller compensate after doing the multiplication.
      *
      * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in
      * particular, to ensure that the outputs from the endomorphism-split fit into
-     * 128 bits). If we negate, the parity of our number flips, affecting whether
-     * we want to add to the scalar to ensure that it's odd. */
-    flip = secp256k1_scalar_is_high(&s);
-    skew = flip ^ secp256k1_scalar_is_even(&s);
-    secp256k1_scalar_cadd_bit(&s, 0, skew);
+     * 128 bits). If we negate, the parity of our number flips, inverting which of
+     * {1, 2} we want to add to the scalar when ensuring that it's odd. Further
+     * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and
+     * we need to special-case it in this logic. */
+    flip = secp256k1_scalar_is_high(scalar);
+    /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */
+    bit = flip ^ !secp256k1_scalar_is_even(scalar);
+    /* We check for negative one, since adding 2 to it will cause an overflow */
+    secp256k1_scalar_negate(&s, scalar);
+    not_neg_one = !secp256k1_scalar_is_one(&s);
+    s = *scalar;
+    secp256k1_scalar_cadd_bit(&s, bit, not_neg_one);
+    /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects
+     * that we added two to it and flipped it. In fact for -1 these operations are
+     * identical. We only flipped, but since skewing is required (in the sense that
+     * the skew must be 1 or 2, never zero) and flipping is not, we need to change
+     * our flags to claim that we only skewed. */
     global_sign = secp256k1_scalar_cond_negate(&s, flip);
+    global_sign *= not_neg_one * 2 - 1;
+    skew = 1 << bit;
 
     /* 4 */
     u_last = secp256k1_scalar_shr_int(&s, w);
@@ -213,22 +214,42 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
         }
     }
 
+    secp256k1_fe_mul(&r->z, &r->z, &Z);
+
     {
         /* Correct for wNAF skew */
+        secp256k1_ge correction = *a;
+        secp256k1_ge_storage correction_1_stor;
+        secp256k1_ge_storage correction_lam_stor;
+        secp256k1_ge_storage a2_stor;
         secp256k1_gej tmpj;
+        secp256k1_gej_set_ge(&tmpj, &correction);
+        secp256k1_gej_double_var(&tmpj, &tmpj, NULL);
+        secp256k1_ge_set_gej(&correction, &tmpj);
+        secp256k1_ge_to_storage(&correction_1_stor, a);
+        if (size > 128) {
+            secp256k1_ge_to_storage(&correction_lam_stor, a);
+        }
+        secp256k1_ge_to_storage(&a2_stor, &correction);
 
-        secp256k1_ge_neg(&tmpa, &pre_a[0]);
-        secp256k1_gej_add_ge(&tmpj, r, &tmpa);
-        secp256k1_gej_cmov(r, &tmpj, skew_1);
+        /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */
+        secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2);
+        if (size > 128) {
+            secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2);
+        }
+
+        /* Apply the correction */
+        secp256k1_ge_from_storage(&correction, &correction_1_stor);
+        secp256k1_ge_neg(&correction, &correction);
+        secp256k1_gej_add_ge(r, r, &correction);
 
         if (size > 128) {
-            secp256k1_ge_neg(&tmpa, &pre_a_lam[0]);
-            secp256k1_gej_add_ge(&tmpj, r, &tmpa);
-            secp256k1_gej_cmov(r, &tmpj, skew_lam);
+            secp256k1_ge_from_storage(&correction, &correction_lam_stor);
+            secp256k1_ge_neg(&correction, &correction);
+            secp256k1_ge_mul_lambda(&correction, &correction);
+            secp256k1_gej_add_ge(r, r, &correction);
         }
     }
-
-    secp256k1_fe_mul(&r->z, &r->z, &Z);
 }
 
 #endif /* SECP256K1_ECMULT_CONST_IMPL_H */
diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h
index 52af941..0d9e55b 100644
--- a/src/ecmult_impl.h
+++ b/src/ecmult_impl.h
@@ -96,6 +96,25 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, sec
     secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z);
 }
 
+/** Fill a table 'pre' with precomputed odd multiples of a.
+ *
+ *  The resulting point set is brought to a single constant Z denominator, stores the X and Y
+ *  coordinates as ge_storage points in pre, and stores the global Z in rz.
+ *  It only operates on tables sized for WINDOW_A wnaf multiples.
+ *
+ *  To compute a*P + b*G, we compute a table for P using this function,
+ *  and use the precomputed table in <precomputed_ecmult.c> for G.
+ */
+static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) {
+    secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)];
+    secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
+
+    /* Compute the odd multiples in Jacobian form. */
+    secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a);
+    /* Bring them to the same Z denominator. */
+    secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr);
+}
+
 /** The following two macro retrieves a particular odd multiple from a table
  *  of precomputed multiples. */
 #define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \
diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h
index aecb611..0dcfa0a 100644
--- a/src/field_10x26_impl.h
+++ b/src/field_10x26_impl.h
@@ -11,15 +11,6 @@
 #include "field.h"
 #include "modinv32_impl.h"
 
-/** See the comment at the top of field_5x52_impl.h for more details.
- *
- *  Here, we represent field elements as 10 uint32_t's in base 2^26, least significant first,
- *  where limbs can contain >26 bits.
- *  A magnitude M means:
- *  - 2*M*(2^22-1) is the max (inclusive) of the most significant limb
- *  - 2*M*(2^26-1) is the max (inclusive) of the remaining limbs
- */
-
 #ifdef VERIFY
 static void secp256k1_fe_verify(const secp256k1_fe *a) {
     const uint32_t *d = a->n;
diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h
index 9b824b9..46599c0 100644
--- a/src/field_5x52_impl.h
+++ b/src/field_5x52_impl.h
@@ -22,18 +22,11 @@
 #endif
 
 /** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F,
- *  represented as 5 uint64_t's in base 2^52, least significant first. Note that the limbs are allowed to
- *  contain >52 bits each.
- *
- *  Each field element has a 'magnitude' associated with it. Internally, a magnitude M means:
- *  - 2*M*(2^48-1) is the max (inclusive) of the most significant limb
- *  - 2*M*(2^52-1) is the max (inclusive) of the remaining limbs
- *
- *  Operations have different rules for propagating magnitude to their outputs. If an operation takes a
- *  magnitude M as a parameter, that means the magnitude of input field elements can be at most M (inclusive).
- *
- *  Each field element also has a 'normalized' flag. A field element is normalized if its magnitude is either
- *  0 or 1, and its value is already reduced modulo the order of the field.
+ *  represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular,
+ *  each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element
+ *  is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations
+ *  accept any input with magnitude at most M, and have different rules for propagating magnitude to their
+ *  output.
  */
 
 #ifdef VERIFY
diff --git a/src/group.h b/src/group.h
index 0b55ad7..b9cd334 100644
--- a/src/group.h
+++ b/src/group.h
@@ -124,9 +124,6 @@ static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge
 /** Convert a group element back from the storage type. */
 static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a);
 
-/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time.  Both *r and *a must be initialized.*/
-static void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag);
-
 /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time.  Both *r and *a must be initialized.*/
 static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag);
 
diff --git a/src/group_impl.h b/src/group_impl.h
index f02891d..bce9fbd 100644
--- a/src/group_impl.h
+++ b/src/group_impl.h
@@ -642,14 +642,6 @@ static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storag
     r->infinity = 0;
 }
 
-static SECP256K1_INLINE void secp256k1_gej_cmov(secp256k1_gej *r, const secp256k1_gej *a, int flag) {
-    secp256k1_fe_cmov(&r->x, &a->x, flag);
-    secp256k1_fe_cmov(&r->y, &a->y, flag);
-    secp256k1_fe_cmov(&r->z, &a->z, flag);
-
-    r->infinity ^= (r->infinity ^ a->infinity) & flag;
-}
-
 static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) {
     secp256k1_fe_storage_cmov(&r->x, &a->x, flag);
     secp256k1_fe_storage_cmov(&r->y, &a->y, flag);
diff --git a/src/precompute_ecmult.c b/src/precompute_ecmult.c
index 5ccbcb3..488ba48 100644
--- a/src/precompute_ecmult.c
+++ b/src/precompute_ecmult.c
@@ -32,7 +32,7 @@ static void print_table(FILE *fp, const char *name, int window_g, const secp256k
 
     j = 1;
     for(i = 3; i <= window_g; ++i) {
-        fprintf(fp, "#if WINDOW_G > %d\n", i-1);
+        fprintf(fp, "#if ECMULT_TABLE_SIZE(WINDOW_G) > %ld\n", ECMULT_TABLE_SIZE(i-1));
         for(;j < ECMULT_TABLE_SIZE(i); ++j) {
             fprintf(fp, ",S(%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32
                           ",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32",%"PRIx32")\n",
@@ -57,8 +57,6 @@ static void print_two_tables(FILE *fp, int window_g) {
 }
 
 int main(void) {
-    /* Always compute all tables for window sizes up to 15. */
-    int window_g = (ECMULT_WINDOW_SIZE < 15) ? 15 : ECMULT_WINDOW_SIZE;
     FILE* fp;
 
     fp = fopen("src/precomputed_ecmult.c","w");
@@ -79,7 +77,7 @@ int main(void) {
     fprintf(fp, "#include \"ecmult.h\"\n");
     fprintf(fp, "#include \"precomputed_ecmult.h\"\n");
     fprintf(fp, "#define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)\n");
-    fprintf(fp, "#if ECMULT_WINDOW_SIZE > %d\n", window_g);
+    fprintf(fp, "#if ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE) > %ld\n", ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE));
     fprintf(fp, "   #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting precomputed_ecmult.c before the build.\n");
     fprintf(fp, "#endif\n");
     fprintf(fp, "#ifdef EXHAUSTIVE_TEST_ORDER\n");
@@ -87,7 +85,7 @@ int main(void) {
     fprintf(fp, "#endif /* EXHAUSTIVE_TEST_ORDER */\n");
     fprintf(fp, "#define WINDOW_G ECMULT_WINDOW_SIZE\n");
 
-    print_two_tables(fp, window_g);
+    print_two_tables(fp, ECMULT_WINDOW_SIZE);
 
     fprintf(fp, "#undef S\n");
     fclose(fp);
diff --git a/src/precomputed_ecmult.c b/src/precomputed_ecmult.c
index 3e67f37..bd7f46a 100644
--- a/src/precomputed_ecmult.c
+++ b/src/precomputed_ecmult.c
@@ -10,7 +10,7 @@
 #include "ecmult.h"
 #include "precomputed_ecmult.h"
 #define S(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) SECP256K1_GE_STORAGE_CONST(0x##a##u,0x##b##u,0x##c##u,0x##d##u,0x##e##u,0x##f##u,0x##g##u,0x##h##u,0x##i##u,0x##j##u,0x##k##u,0x##l##u,0x##m##u,0x##n##u,0x##o##u,0x##p##u)
-#if ECMULT_WINDOW_SIZE > 15
+#if ECMULT_TABLE_SIZE(ECMULT_WINDOW_SIZE) > 8192
    #error configuration mismatch, invalid ECMULT_WINDOW_SIZE. Try deleting precomputed_ecmult.c before the build.
 #endif
 #ifdef EXHAUSTIVE_TEST_ORDER
@@ -19,20 +19,20 @@
 #define WINDOW_G ECMULT_WINDOW_SIZE
 const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
  S(79be667e,f9dcbbac,55a06295,ce870b07,29bfcdb,2dce28d9,59f2815b,16f81798,483ada77,26a3c465,5da4fbfc,e1108a8,fd17b448,a6855419,9c47d08f,fb10d4b8)
-#if WINDOW_G > 2
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 1
 ,S(f9308a01,9258c310,49344f85,f89d5229,b531c845,836f99b0,8601f113,bce036f9,388f7b0f,632de814,fe337e6,2a37f356,6500a999,34c2231b,6cb9fd75,84b8e672)
 #endif
-#if WINDOW_G > 3
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 2
 ,S(2f8bde4d,1a072093,55b4a725,a5c5128,e88b84bd,dc619ab7,cba8d569,b240efe4,d8ac2226,36e5e3d6,d4dba9dd,a6c9c426,f788271b,ab0d6840,dca87d3a,a6ac62d6)
 ,S(5cbdf064,6e5db4ea,a398f365,f2ea7a0e,3d419b7e,330e39c,e92bdded,cac4f9bc,6aebca40,ba255960,a3178d6d,861a54db,a813d0b8,13fde7b5,a5082628,87264da)
 #endif
-#if WINDOW_G > 4
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 4
 ,S(acd484e2,f0c7f653,9ad178a,9f559abd,e0979697,4c57e714,c35f110d,fc27ccbe,cc338921,b0a7d9fd,64380971,763b61e9,add888a4,375f8e0f,5cc262a,c64f9c37)
 ,S(774ae7f8,58a9411e,5ef4246b,70c65aac,5649980b,e5c17891,bbec1789,5da008cb,d984a032,eb6b5e19,243dd56,d7b7b365,372db1e2,dff9d6a8,301d74c9,c953c61b)
 ,S(f28773c2,d975288b,c7d1d205,c3748651,b075fbc6,610e58cd,deeddf8f,19405aa8,ab0902e,8d880a89,758212eb,65cdaf47,3a1a06da,521fa91f,29b5cb52,db03ed81)
 ,S(d7924d4f,7d43ea96,5a465ae3,95ff411,31e5946f,3c85f79e,44adbcf8,e27e080e,581e2872,a86c72a6,83842ec2,28cc6def,ea40af2b,d896d3a5,c504dc9f,f6a26b58)
 #endif
-#if WINDOW_G > 5
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 8
 ,S(defdea4c,db677750,a420fee8,7eacf21,eb9898ae,79b97687,66e4faa0,4a2d4a34,4211ab06,94635168,e997b0ea,d2a93dae,ced1f4a0,4a95c0f6,cfb199f6,9e56eb77)
 ,S(2b4ea0a7,97a443d2,93ef5cff,444f4979,f06acfeb,d7e86d27,74756561,38385b6c,85e89bc0,37945d93,b343083b,5a1c8613,1a01f60c,50269763,b570c854,e5c09b7a)
 ,S(352bbf4a,4cdd1256,4f93fa33,2ce33330,1d9ad402,71f81071,81340aef,25be59d5,321eb407,5348f534,d59c1825,9dda3e1f,4a1b3b2e,71b1039c,67bd3d8b,cf81998c)
@@ -42,7 +42,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(c44d12c7,65d812e,8acf28d7,cbb19f90,11ecd9e9,fdf281b0,e6a3b5e8,7d22e7db,2119a460,ce326cdc,76c45926,c982fdac,e106e86,1edf61c5,a039063f,e0e6482)
 ,S(6a245bf6,dc698504,c89a20cf,ded60853,152b6953,36c28063,b61c65cb,d269e6b4,e022cf42,c2bd4a70,8b3f5126,f16a24ad,8b33ba48,d0423b6e,fd5e6348,100d8a82)
 #endif
-#if WINDOW_G > 6
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 16
 ,S(1697ffa6,fd9de627,c077e3d2,fe541084,ce13300b,bec1146,f95ae57f,d0bd6a5,b9c398f1,86806f5d,27561506,e4557433,a2cf1500,9e498ae7,adee9d63,d01b2396)
 ,S(605bdb01,9981718b,986d0f07,e834cb0d,9deb8360,ffb7f61d,f982345e,f27a7479,2972d2d,e4f8d206,81a78d93,ec96fe23,c26bfae8,4fb14db4,3b01e1e9,56b8c49)
 ,S(62d14dab,4150bf49,7402fdc4,5a215e10,dcb01c35,4959b10c,fe31c7e9,d87ff33d,80fc06bd,8cc5b010,98088a19,50eed0db,1aa1329,67ab4722,35f56424,83b25eaf)
@@ -60,7 +60,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(754e3239,f325570c,dbbf4a87,deee8a66,b7f2b334,79d468fb,c1a50743,bf56cc18,673fb86,e5bda30f,b3cd0ed3,4ea49a0,23ee33d0,197a695d,c5d9809,3c536683)
 ,S(e3e6bd10,71a1e96a,ff57859c,82d570f0,33080066,1d1c952f,9fe26946,91d9b9e8,59c9e0bb,a394e76f,40c0aa58,379a3cb6,a5a22839,93e90c41,67002af4,920e37f5)
 #endif
-#if WINDOW_G > 7
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 32
 ,S(186b483d,56a0338,26ae73d8,8f732985,c4ccb1f3,2ba35f4b,4cc47fdc,f04aa6eb,3b952d32,c67cf77e,2e17446e,204180ab,21fb8090,895138b4,a4a797f8,6e80888b)
 ,S(df9d70a6,b9876ce5,44c98561,f4be4f72,5442e6d2,b737d9c9,1a832172,4ce0963f,55eb2daf,d84d6ccd,5f862b78,5dc39d4a,b1572227,20ef9da2,17b8c45c,f2ba2417)
 ,S(5edd5cc2,3c51e87a,497ca815,d5dce0f8,ab52554f,849ed899,5de64c5f,34ce7143,efae9c8d,bc141306,61e8cec0,30c89ad0,c13c66c0,d17a2905,cdc706ab,7399a868)
@@ -94,7 +94,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(c4191636,5abb2b5d,9192f5f,2dbeafec,208f020f,12570a18,4dbadc3e,58595997,4f14351,d0087efa,49d245b3,28984989,d5caf945,f34bfc0,ed16e96b,58fa9913)
 ,S(841d6063,a586fa47,5a724604,da03bc5b,92a2e0d2,e0a36acf,e4c73a55,14742881,73867f5,9c0659e8,1904f9a1,c7543698,e62562d6,744c169c,e7a36de0,1a8d6154)
 #endif
-#if WINDOW_G > 8
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 64
 ,S(5e95bb39,9a6971d3,76026947,f89bde2f,282b3381,928be4d,ed112ac4,d70e20d5,39f23f36,6809085b,eebfc711,81313775,a99c9aed,7d8ba38b,161384c7,46012865)
 ,S(36e4641a,53948fd4,76c39f8a,99fd974e,5ec07564,b5315d8b,f99471bc,a0ef2f66,d2424b1b,1abe4eb8,164227b0,85c9aa94,56ea1349,3fd563e0,6fd51cf5,694c78fc)
 ,S(336581e,a7bfbbb2,90c191a2,f507a41c,f5643842,170e914f,aeab27c2,c579f726,ead12168,595fe1be,99252129,b6e56b33,91f7ab14,10cd1e0e,f3dcdcab,d2fda224)
@@ -160,7 +160,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(809a20c6,7d64900f,fb698c4c,825f6d5f,2310fb04,51c86934,5b7319f6,45605721,9e994980,d9917e22,b76b0619,27fa0414,3d096ccc,54963e6a,5ebfa5f3,f8e286c1)
 ,S(1b38903a,43f7f114,ed4500b4,eac7083f,defece1c,f29c6352,8d563446,f972c180,4036edc9,31a60ae8,89353f77,fd53de4a,2708b26b,6f5da72a,d3394119,daf408f9)
 #endif
-#if WINDOW_G > 9
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 128
 ,S(90a80db6,eb294b9e,ab0b4e8d,dfa3efe7,263458ce,2d07566d,f4e6c588,68feef23,753c8b9f,9754f18d,87f21145,d9e2936b,5ee050b2,7bbd9681,442c76e9,2fcf91e6)
 ,S(c2c80f84,4b705998,12d62546,f60340e,3e6f3605,4a14546e,6dc25d47,376bea9b,86ca160d,68f4d4e7,18b495b8,91d3b1b5,73b871a7,2b4cf61,23abd448,3aa79c64)
 ,S(9cf60674,4cf4b5f3,fdf989d3,f19fb265,2d00cfe1,d5fcd692,a323ce11,a28e7553,8147cbf7,b973fcc1,5b57b6a3,cfad6863,edd0f30e,3c45b85d,c300c513,c247759d)
@@ -290,7 +290,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(367807c9,a3606b4e,1b8c2616,ad528030,1dfcf686,40eddf02,fc59317c,230e9a86,1f023f2f,a2bbece7,3dba14c,124095cb,fdc4f92f,281a14,8304a412,c16ecae6)
 ,S(8ec4fdc3,9891f6af,1374e06f,c44b815,1b82541,75fc4909,acba5941,201af62b,2dc6cae5,cac2d887,83dca0e5,3c798f8,fe067bcf,5fc29751,13756cf7,ef4e5f1b)
 #endif
-#if WINDOW_G > 10
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 256
 ,S(5cc24a6d,4c5b24f9,14542f91,e5fa937f,ffa08551,51b8b842,8729b06a,9178a263,b1da8635,81531a1f,bfb38a4e,419fa1fe,ca8d55a8,3ddbcb98,da19d5cf,fb7da472)
 ,S(83905926,c03905c3,a9644a6c,da810dd2,92602a50,50c52a21,9134fc4d,e3599e9f,4293260f,e8af6792,a20b115e,aa837638,9094298b,21d9de16,cf20e0c5,7a46089a)
 ,S(944b097e,4721e9dd,f8204ac3,d3878fa,e8fa6c14,34ae4822,481b2985,6589b6c7,5fc47565,30e9b095,f8b79643,1e745b99,1525bd4c,4764e8e,e8af4b96,9bd6ddf6)
@@ -548,7 +548,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(d9a0c689,95283291,972d6a72,897181b2,6b4ae317,4e98a676,f75bbfbf,81be876e,d36ae886,a0acbc9a,d1564d97,4d3f0309,e9039b93,bb350d92,2b7f8c7a,c6bfa042)
 ,S(c7a36324,6aeb7c8c,991b2aa7,10abdf5c,fff29912,30b3a69f,be2dd481,7c7c3e0a,1298fdd7,e448d2d,79986302,6b4b2d49,2b32148,d6d1e5f8,15f5b0c0,5c9e21f)
 #endif
-#if WINDOW_G > 11
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 512
 ,S(635cd7a0,5064d3bc,66535a0,4dbf563a,640d2464,c7fe0ac4,8304214f,e4985a86,e40265,913e77f6,46735cfc,af9e2f30,e8d5f047,f3281a4c,e0453e27,e9e3ae1f)
 ,S(5da624f,38edea25,37f5b632,695b481a,eca54bb7,2169cadc,c5b91e10,989ee5f5,d3ea6dba,a1080300,fffaeeb2,43a5256e,48c28822,37b16505,a220d771,ca721779)
 ,S(aa4f64a2,b19775ec,92c1f687,1fe4b6d5,ce05278c,2976c80e,fe19284f,e87d4d5e,f39f117e,95fce0fb,6976e949,9583a66c,224ea028,8396518e,293793dc,3ed4f240)
@@ -1062,7 +1062,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(dd879eba,f870ee3d,f6e3f03,60833e12,fbdf844c,d6a5b76c,19802485,6649bbc1,e7bb04c3,a99a5c11,dd7a324c,1d5696c8,b041a12a,c6a538f7,3094716c,9943c55a)
 ,S(7aa4afbe,29b06a1e,da9319d5,72572b13,1df68f74,1b5f8d8e,d00ccc04,80f8016e,15f79a9f,b77b8587,7f02e3c1,3202b972,423fd00d,937c0d2d,c9d94b17,53fe7130)
 #endif
-#if WINDOW_G > 12
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 1024
 ,S(9145f3f5,876a3265,5d7fee64,f2d6e660,c34354e9,1571d68b,a19e4cc5,8c39f890,45e0a95d,cf369fd0,5bc9ba7f,da108d6f,37650c1d,5766b6c9,98ecda28,b285d8ff)
 ,S(5450b752,36fb010d,1ef37afa,2077f3dc,a5a7f6c8,91a21317,13df740f,4511ac9e,a7c7ed57,62bef86d,4de1084d,3ded2d7b,13ff563a,67109ef0,8b6f0180,fe6ba175)
 ,S(ba6c0d72,5e48de2c,cb8256d6,7417d075,bbf2766f,d13501b8,4c32cf88,c0666a0f,cd2a9132,7137299f,50eda669,3a599032,bdecd64b,91bcc640,5ed186f3,e525e442)
@@ -2088,7 +2088,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(6d109d8c,2505808f,7b4052d6,b170ec80,c68373bb,e02f2b62,1cd29773,a96acb3e,c8dea0de,511f6e40,5141088e,cb023bff,200c870b,9cb952ad,c5038b28,eadc9a57)
 ,S(9c219898,6d202467,c055c734,73557505,6105b3e,2874dbf2,8f7f0c39,66e1af88,8637496c,8eddff12,f9dd4146,36565e0f,efddf5cd,52d48455,ec9fb2d,8dd0914a)
 #endif
-#if WINDOW_G > 13
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 2048
 ,S(22ca5039,e660f60,1036dd75,2bb973b,dcd104b5,dcb8f2e7,4de8edc6,c292b03a,86fd4471,1ef6b81e,4416449a,708fa0c9,cb6731ba,c403e58e,da5e915f,646b11e8)
 ,S(cfc18f02,cc004640,f2116fdd,1f6ca202,2e39be25,df75e27c,80bde842,e6b6f938,d62b58df,4f79d0e5,97ba2923,f708cbe5,c09236a4,d9d01398,6451d684,6290df7d)
 ,S(3388bcc2,3425458e,991f46ca,6235c50a,57490556,becbaf25,9d3c14f9,9a80a087,7d4aa2f8,6f65783,1c22316,6958fbf3,6c3dca5,d4ac413b,3102e13e,a645c016)
@@ -4138,7 +4138,7 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(d5acff71,e82a455c,3a1ce292,689e8686,f441ce1b,e644e79a,bd6d0efe,29270865,aac6d48f,1b46e970,a044971a,ad13f033,ca8cde96,958d870e,dc7d80,1d26d5e8)
 ,S(5cf51c1b,2210d85,9d765e17,32109514,8f03fc57,51004b6a,91f098e2,e2711596,1eeb19e0,610df459,2c31e58e,aa2a2148,17fe9ee,a3995838,f395bdcf,26d5c3b3)
 #endif
-#if WINDOW_G > 14
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 4096
 ,S(21456873,b58e3687,52c75800,8d3bcae,9efaf1f5,c5727842,25e3d854,8fd421cb,a2f2d10,c85e8a0f,4a136ad0,5df991ce,7d3c5585,a263d5cf,da50a4f4,3db76cb0)
 ,S(c10de5c0,51ae2d73,28ac06ca,cca840b4,ed7ab204,21c6122f,1d68fe7f,7893d38d,ee30e086,a891484,2ad4041,f9ab9c57,cff1a315,f0642d31,b31c2914,faac99e0)
 ,S(be778032,f12c1b77,9bba3d9e,d290ca90,30ac7050,bdd77a2,7eac09be,eea65c0b,8657348b,a1e27a63,1dd2a54b,e2d5270f,4cca817a,219c5378,4d4f73ba,2c932e63)
@@ -8239,20 +8239,20 @@ const secp256k1_ge_storage secp256k1_pre_g[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 };
 const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
  S(8f68b9d2,f63b5f33,9239c1ad,981f162e,e88c5678,723ea335,1b7b444c,9ec4c0da,662a9f2d,ba063986,de1d90c2,b6be215d,bbea2cfe,95510bfd,f23cbf79,501fff82)
-#if WINDOW_G > 2
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 1
 ,S(38381dbe,2e509f22,8ba93363,f2451f08,fd845cb3,51d954be,18e2b8ed,d23809fa,e4a32d0a,fb917dc,b09405a5,520eb1cc,3681fccb,32d8f24d,bd707518,331fed52)
 #endif
-#if WINDOW_G > 3
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 2
 ,S(49262724,e4372ae6,f6921b82,aa4699a1,f186aea5,40122630,3ea42648,97c2a310,1337e773,bca7abf9,5a2cfa56,9714303b,6d163612,a75ff8ce,c41b681,5e27ded0)
 ,S(e306568c,1a240c90,d5e253b3,e477e2f8,4dcc1a56,ff06db8d,1384b079,cebd2d31,eac6fe3,78934260,888f2b10,7f7d0db6,ffbc8042,be373826,692b4083,92546e44)
 #endif
-#if WINDOW_G > 4
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 4
 ,S(3b9e100e,2428cefc,271b0e76,23fbd633,74ebf8d9,aab41dd9,c530c39e,363136b0,fafb9815,2d16bb71,df1533eb,8f475b26,a2ae28a3,3ad31f81,953ec16f,6cdbbc8a)
 ,S(bb0aad49,712ac9a9,2b76ca80,f5dedef7,17ca0768,8107beee,9608f047,2f485d3f,ea699c53,c5835479,8ecd201f,7297da34,895a5afa,31670bff,e7939250,3ca2f975)
 ,S(79090ac8,e4eefcc0,d4e8eb19,7afe0113,e1e58b4d,b01123de,4aeed33a,36718dc9,eaab722b,91905b8f,13d816cb,cd9aaa56,dd36afb7,ba9008b,963322b1,1cfae7c5)
 ,S(e77c81ad,e9f97b55,1c03dbbc,e549ba66,8dd71de7,cd775ad2,a269694c,7f60c7d1,3acf1478,eef81321,c5fc3b32,3ea81543,631470f7,1c2986d3,4ec581f2,82d72449)
 #endif
-#if WINDOW_G > 5
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 8
 ,S(de2b5ce9,dbce511c,f2d8878e,3ded87cc,3d633dae,a2d45341,501fb3a4,55ccf6b0,f10576f3,d3c3e0e1,bbf717e9,8b1a3744,65b8c45a,c66318bb,34829eb7,11100666)
 ,S(d07bddff,d491a2fe,1ea59fbd,7c121217,29659ca5,de46658b,26b1460b,13c03c56,b2ad4708,cd3c97dd,f9c40e2,a1de04d5,61d963ff,8cc2eea7,6be3f60c,2b405ce7)
 ,S(82403e7c,5d3016af,3765ec4c,396ce8e1,f8da45c,434b8257,10edab41,bb6a4d51,d09661b,e27cb767,4456badd,b3e84051,99ab6ccc,4ec67c1b,11e92ead,7b463b19)
@@ -8262,7 +8262,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(1f56f096,b18a7499,a153a5ae,acf8be05,8496dd23,da8e6c19,215628fb,c0567ed0,fef22b8a,3b52f490,83004436,b65cd69,c94189f4,1a93c0d5,1fc13cb4,379dff58)
 ,S(1d9f69b1,a4a47432,e386f525,234aa30,79e947cf,cf203297,4e0fc05b,638e213b,d898ec17,949c0761,b38500c3,a2b1da24,5438d5b3,d3f6f720,41f15d6e,e4d4ccbb)
 #endif
-#if WINDOW_G > 6
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 16
 ,S(128c913,4d9dcb78,12fc4361,5c67ad0,55213354,dc8008b1,aeb5a9dc,fb629efd,fee3e54a,dd152610,d9725936,99d662,c160c8e4,ec6f76e4,5ff41818,be67c96)
 ,S(21ec012f,5a95b94d,244b8d51,9756075c,301f2854,8e2c51fc,49c0e3d9,d1a9685,2def2105,77af497f,4c7fef71,6949f28e,7418eda6,fd5fc162,d128de19,3cde08ae)
 ,S(688f5202,fb9d8bc0,9e480e89,4c7cfc74,761c3be7,7dafb11c,58422836,3e331cc5,96ba7d59,63b541a7,2ec7cabd,92403434,1a393eaf,89eebd94,62d9c218,c7302cd3)
@@ -8280,7 +8280,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(144e88f6,3e73abff,72cac11e,7ddccf79,19e744e6,278941ae,18d1b797,e098e4e5,63cdbf3,5df3c655,c58197f,ea54633d,158705cf,7dc2eb3b,4e09f83c,3021837c)
 ,S(9436e3dc,489ecd8e,2d16a739,c9c73e3d,60e5bc93,68157039,75b8efbd,5c3a9081,1460531f,50cb6ebf,d1aa7806,ea84e7f7,8e8d76b2,b3a66d5e,3a0bf60,39a7e59c)
 #endif
-#if WINDOW_G > 7
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 32
 ,S(9d3c2561,7a56d10b,46d9b01a,1710d193,e840e005,df669e76,1936c275,20890db9,6bbdc0bc,4c4ae9bc,c2dfee9b,82da9b94,1f89ffcd,e8af2aca,4467ce3,78521ea3)
 ,S(29f98e50,f51b7f8b,e18c6ae0,b453c4f2,d0aca5a8,b0e61d2d,dda8506,3fdb76c8,daf3bcdc,ae8e031c,73eb8b21,14058063,58a6ec30,ad379186,df80e3c7,f0e5d28f)
 ,S(d67d30c2,c71daa36,1805e31,1dd6046e,17a89752,94d76e1a,538af074,4dc22c94,48b9b0e7,12c807b0,b92e690a,a2e068cc,e87ebbbe,aaf4bd96,9c1114bb,a54f670c)
@@ -8314,7 +8314,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(6f97ac0f,27b87905,6b442d13,e566978e,91f0cc1d,d6ac1e64,e9764a35,325dd1b7,83c6e70c,fac6c707,226ce1cc,691b38a0,7e937f5a,5f2d9c81,4dd0d3ff,9f433d32)
 ,S(72c5d60f,eb014e2d,ba8265ca,d454f261,2d6abcd4,b2236bad,c94c4801,561dce1f,e3119a19,7ef91963,b3b28216,3c5d3acb,97b281b6,d246cbf0,690b40da,63978fe2)
 #endif
-#if WINDOW_G > 8
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 64
 ,S(a96d2da0,1b10186,6998659d,f441a1b0,2af32b94,aae8c6ea,707d9ed0,d5f33825,660d7d83,5da5235,9f7cfd41,28c370aa,5659ea71,16a91690,6c0e8108,a513f9f)
 ,S(2cce6f63,4d815ecc,1981d200,87616677,d906aa27,990c4875,17314dc5,5be3c4fa,615210dd,bd599e91,1b6f997a,fd05475,b33cb274,c9ecb6e5,d3c23323,beba4b50)
 ,S(992b0084,525dd399,d98602d9,8b8d53b2,4558fafc,758a2f46,60e89bd6,a645f0c4,83ca98d2,26545a29,8c45f40b,11420602,f5a5c70e,595eea57,2da64d61,a4e2f98d)
@@ -8380,7 +8380,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(7590a4a,627d5e90,e42a4b0f,6be6497a,f906182d,d2dddc48,a8b6bbfd,f56c0504,d611e21a,b5498760,5c97e878,baa464bc,cc5bd875,353b69f8,1f93ce2a,a6c38587)
 ,S(94b1da0f,b310e056,db0dff72,81db3362,1fcc555d,bf3c973b,76097908,7fe19d6a,8318893d,d5b41a56,33a2ab4,ae4b953c,45c42e9c,8f2fd159,85286de3,fd4fc217)
 #endif
-#if WINDOW_G > 9
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 128
 ,S(b5af4299,bcdacef1,e07d081,daec2cfa,d6f8f821,38e151a5,f20e6d52,84c9a6cb,c0984407,8a7db82d,f572987e,b137dc09,c8cf65fd,aedcb20,43b2479b,ab95448d)
 ,S(5f49ad43,70744587,3980a153,c851829b,f8ef6141,9889bb0c,3c476847,6939c3e3,5c40d385,20f56c3d,ba08ae1,b40fc24a,2ae25c94,45cae0f7,d01d1800,747e04eb)
 ,S(f6bb067f,88ccc11c,64e30d1a,e6893942,16bea3bf,26ec9c64,5cde1b9e,487da385,315a476d,7268978c,d89d4ec8,adde4a83,28dbfdd9,f2bf44fe,ad4ed721,78288f55)
@@ -8510,7 +8510,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(8ac79852,75673818,69fe93c9,6141493e,72ca344,790487b,d5425ed6,9f5c5c18,bb314248,fcbfc867,d1972e04,b9ef1f90,775375f9,9d25bcec,684c72c9,bdd1c08e)
 ,S(560e9711,88cbc7ce,c61f3bf4,45f0ade3,6f3f174d,219a160,5f9c8692,3f848b9d,9e92dace,6775cc67,bfbbf21f,c64e6e9d,4d133f8a,c18ee277,bc80ef6d,d1b9cd2f)
 #endif
-#if WINDOW_G > 10
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 256
 ,S(8c464204,4b95eb66,cc95ca08,8469a1c0,28eea52f,7f709fe,e6d90700,f5fb943a,bf10fc5d,7bc25181,301b3a61,5ebea597,a3492025,953c3aa5,1a11271e,689d0223)
 ,S(d293f74c,f3578b71,35377247,cd8b0367,7f2245da,f87453cb,4d8d1cc2,871eb5aa,86c2013a,61dfea14,63e45931,eb09050f,f080e3d4,ae357423,ba7afbed,a64a6f26)
 ,S(35997241,ae757b1f,c767611e,c76eb935,fefbf7a7,33666aff,4c6bd744,d7687d35,bcbea61f,246bcd4c,c3c7fd35,7bd393da,2f36e0ef,cca0df9c,5994f96f,c44b2aa0)
@@ -8768,7 +8768,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(6ca7e032,8bf5d67a,b804d431,e5f709e,bd3156e0,1d4511da,1dc67394,24d2e659,c428b133,f3683909,6551c2ff,2f870d80,d80aaaf4,6d2b1a69,7722057,5eca2647)
 ,S(60df19a9,83e9086c,4cbd20ba,fbafa8b2,346ae4ee,9c1ad4dd,99959e91,2df530d3,dae7b854,29f817a0,421c2f1e,e4e8e4d,a09d60f8,84701e31,d7a8b7b0,3b79cc48)
 #endif
-#if WINDOW_G > 11
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 512
 ,S(6f18d50e,5ef5f2ad,bad80ea1,c59a3847,5c22ff05,6ba52c7,e1d26d6d,d3686bce,d1d7ea0f,a166efd0,facf5c83,bc3786e0,d3f3405d,5b4578f5,13a336ad,f0d7d7a9)
 ,S(b634dc9d,5ed51336,4ac9569a,8ddee9d9,fcdfe00e,65e59233,b3be8a07,2fd949e0,d72e46e2,c61b2575,f25a505c,bdc3456e,fdf18976,6562a1a7,e86d036,eb31db69)
 ,S(5a37fcf0,2dba24f3,e6646171,43dbc5ab,40d91983,69f5cf0e,4fc566fd,97445910,a960d1d8,13f8746c,662a9582,614c7847,33e6153d,2975cb59,c3342463,75c69d1e)
@@ -9282,7 +9282,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(edc9e8b1,3c61a1e0,8f93be12,495084ed,b5a90eca,4f11dc03,cd217b1a,56285a2c,66c2fd21,7e8fac7,4fd43b1b,58c80661,b0e8df85,a2fddfda,5e9f99fe,621e1f3d)
 ,S(dd94295f,8388a498,2aaa4164,927b81ba,cae9d002,b3ddb6b6,97082c70,f1e2c66e,838c060c,d409c1ea,9bcd9e8c,b1fac83f,3b08c2b2,482d8f4b,fd3ebc18,8d6706b)
 #endif
-#if WINDOW_G > 12
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 1024
 ,S(7e068e04,dfc0be48,e5c0d3b3,5bfc734e,96e96ddd,d0ac4876,92f74535,685ab7e,df2cd146,90d225c8,d04052e6,93f14bf,69351e08,79883646,2c88401e,4ec70d0a)
 ,S(73a9bfcf,d10aac9b,46e659f,76c439a0,b7c2a073,7dec217a,21f43f39,949a5052,73b91529,3ea7b052,682062c,8f86cbf,bf379df6,91a9cec2,4a1424a9,b3be10dd)
 ,S(efddff84,c8cc754,e6e34678,d85809bc,55cc224b,f69be05a,daa847ec,d408c55b,65dd8f41,8264aab2,efe6ed7e,c45b4ac,8aac218a,3b6713fc,1736dd6d,c2f188d5)
@@ -10308,7 +10308,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(8a663e7,40cad237,12dad6f9,1bb5d266,30542933,853e02eb,665e198d,a5c6e53b,e206b399,51175d15,daccf711,63b0918c,786a8fe,2077812,c0c1eee6,bb5c1e99)
 ,S(2b0956cb,e0ca91ee,f44fac0a,76cc0b00,5f4a5ae8,27e63696,fe1aa8b4,a92941a1,a31e6fa8,dd454eaf,90ee1e11,62627e3,3f84b8fe,da29f423,ddf2a962,386cffd2)
 #endif
-#if WINDOW_G > 13
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 2048
 ,S(e0144151,7a2ac970,eb6381df,7e11fb4c,6b009980,6a0d330,d5429126,e662c3bd,8238ce9b,a691c80c,21563934,18d18dbf,aeb3fd34,48863a1e,7f4ba360,408dfcee)
 ,S(be3213e,8b062f33,caf16c53,5a0b666,f344e0d7,1e28aeba,8b215a3,7ec86c37,552523ba,eaca38a4,cd795683,852a2643,550fb83d,d1db0adc,3f1b29c9,7d51cd1f)
 ,S(623393b6,bfbfbd64,fc7aa1db,9e58e274,1c18eb6d,b5eb30ab,c4fe167e,a9e8ff2b,7c0e4174,f0fd5bf2,a025e316,fa3b7b97,1339a197,b52b0b50,bad4dfe,34e42cd3)
@@ -12358,7 +12358,7 @@ const secp256k1_ge_storage secp256k1_pre_g_128[ECMULT_TABLE_SIZE(WINDOW_G)] = {
 ,S(4beaa213,3c6161d0,c7da6d7c,8eb09cba,2e8ca505,a790a7d3,9d0c16b,8053bb52,da4c083c,9cbdda2e,c6626d8f,e4b2ee13,703496c,c298bffa,db1acdca,99d4f6d5)
 ,S(a8d31ec6,53ce1834,a78de363,c5f9abd1,8594b34,4d34f5fc,8a10e81f,5804831,b4d6a9cd,7cbbe370,9edb5601,cb7c8ba4,8c462c18,594a4bce,64f4c286,dac5cff9)
 #endif
-#if WINDOW_G > 14
+#if ECMULT_TABLE_SIZE(WINDOW_G) > 4096
 ,S(a8f11586,f3df4945,a753c485,ee0fd4d,e410771d,bddc1b26,c9ff10e0,77b915b7,a4ad6f16,dbd741bc,71b2dfd2,dd2d340,3816bf73,4e73cc10,abcfa6bb,d0f161a4)
 ,S(13e697c3,812d4772,254082da,372892d4,e1a66e1a,eb16bbd4,f7a0d531,c979cb2,87fa7baa,f6def12d,31e42c14,f672c0d6,a9d0e2e1,ceee2546,d65bd01,c18df57f)
 ,S(3cae4590,821a9697,a5963269,b44f0222,98f60021,b6048b3f,49c6fd4d,8650c7a,e03f8745,60418449,ad97f28,41664745,349329d,268c1c43,86e25147,6e44b234)
diff --git a/src/secp256k1.c b/src/secp256k1.c
index 8f34c35..36fde24 100644
--- a/src/secp256k1.c
+++ b/src/secp256k1.c
@@ -423,12 +423,8 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
    unsigned int offset = 0;
    secp256k1_rfc6979_hmac_sha256 rng;
    unsigned int i;
-   secp256k1_scalar msg;
-   unsigned char msgmod32[32];
-   secp256k1_scalar_set_b32(&msg, msg32, NULL);
-   secp256k1_scalar_get_b32(msgmod32, &msg);
    /* We feed a byte array to the PRNG as input, consisting of:
-    * - the private key (32 bytes) and reduced message (32 bytes), see RFC 6979 3.2d.
+    * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d.
     * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data.
     * - optionally 16 extra bytes with the algorithm name.
     * Because the arguments have distinct fixed lengths it is not possible for
@@ -436,7 +432,7 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m
     *  nonces.
     */
    buffer_append(keydata, &offset, key32, 32);
-   buffer_append(keydata, &offset, msgmod32, 32);
+   buffer_append(keydata, &offset, msg32, 32);
    if (data != NULL) {
        buffer_append(keydata, &offset, data, 32);
    }


Test patch:
diff --git a/src/modules/schnorrsig/tests_impl.h b/src/modules/schnorrsig/tests_impl.h
index 7c4321f..2efec8a 100644
--- a/src/modules/schnorrsig/tests_impl.h
+++ b/src/modules/schnorrsig/tests_impl.h
@@ -87,7 +87,7 @@ void run_nonce_function_bip340_tests(void) {
     CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, NULL, 0, NULL) == 0);
     CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1);
     /* Other algo is fine */
-    secp256k1_testrand_bytes_test(algo, algolen);
+    secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, algo, algolen);
     CHECK(nonce_function_bip340(nonce, msg, msglen, key, pk, algo, algolen, NULL) == 1);
 
     for (i = 0; i < count; i++) {
@@ -795,18 +795,18 @@ void test_schnorrsig_sign_verify(void) {
         /* Flip a few bits in the signature and in the message and check that
          * verify and verify_batch (TODO) fail */
         size_t sig_idx = secp256k1_testrand_int(N_SIGS);
-        size_t byte_idx = secp256k1_testrand_bits(5);
+        size_t byte_idx = secp256k1_testrand_int(32);
         unsigned char xorbyte = secp256k1_testrand_int(254)+1;
         sig[sig_idx][byte_idx] ^= xorbyte;
         CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
         sig[sig_idx][byte_idx] ^= xorbyte;
 
-        byte_idx = secp256k1_testrand_bits(5);
+        byte_idx = secp256k1_testrand_int(32);
         sig[sig_idx][32+byte_idx] ^= xorbyte;
         CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
         sig[sig_idx][32+byte_idx] ^= xorbyte;
 
-        byte_idx = secp256k1_testrand_bits(5);
+        byte_idx = secp256k1_testrand_int(32);
         msg[sig_idx][byte_idx] ^= xorbyte;
         CHECK(!secp256k1_schnorrsig_verify(ctx, sig[sig_idx], msg[sig_idx], sizeof(msg[sig_idx]), &pk));
         msg[sig_idx][byte_idx] ^= xorbyte;
diff --git a/src/testrand.h b/src/testrand.h
index bd149bb..667d186 100644
--- a/src/testrand.h
+++ b/src/testrand.h
@@ -17,14 +17,11 @@
 SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16);
 
 /** Generate a pseudorandom number in the range [0..2**32-1]. */
-SECP256K1_INLINE static uint32_t secp256k1_testrand32(void);
-
-/** Generate a pseudorandom number in the range [0..2**64-1]. */
-SECP256K1_INLINE static uint64_t secp256k1_testrand64(void);
+static uint32_t secp256k1_testrand32(void);
 
 /** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or
  *  more. */
-SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits);
+static uint32_t secp256k1_testrand_bits(int bits);
 
 /** Generate a pseudorandom number in the range [0..range-1]. */
 static uint32_t secp256k1_testrand_int(uint32_t range);
diff --git a/src/testrand_impl.h b/src/testrand_impl.h
index e9b9d7d..c8d30ef 100644
--- a/src/testrand_impl.h
+++ b/src/testrand_impl.h
@@ -14,64 +14,37 @@
 #include "testrand.h"
 #include "hash.h"
 
-static uint64_t secp256k1_test_state[4];
+static secp256k1_rfc6979_hmac_sha256 secp256k1_test_rng;
+static uint32_t secp256k1_test_rng_precomputed[8];
+static int secp256k1_test_rng_precomputed_used = 8;
 static uint64_t secp256k1_test_rng_integer;
 static int secp256k1_test_rng_integer_bits_left = 0;
 
 SECP256K1_INLINE static void secp256k1_testrand_seed(const unsigned char *seed16) {
-    static const unsigned char PREFIX[19] = "secp256k1 test init";
-    unsigned char out32[32];
-    secp256k1_sha256 hash;
-    int i;
-
-    /* Use SHA256(PREFIX || seed16) as initial state. */
-    secp256k1_sha256_initialize(&hash);
-    secp256k1_sha256_write(&hash, PREFIX, sizeof(PREFIX));
-    secp256k1_sha256_write(&hash, seed16, 16);
-    secp256k1_sha256_finalize(&hash, out32);
-    for (i = 0; i < 4; ++i) {
-        uint64_t s = 0;
-        int j;
-        for (j = 0; j < 8; ++j) s = (s << 8) | out32[8*i + j];
-        secp256k1_test_state[i] = s;
-    }
-    secp256k1_test_rng_integer_bits_left = 0;
-}
-
-SECP256K1_INLINE static uint64_t rotl(const uint64_t x, int k) {
-    return (x << k) | (x >> (64 - k));
+    secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16);
 }
 
-SECP256K1_INLINE static uint64_t secp256k1_testrand64(void) {
-    /* Test-only Xoshiro256++ RNG. See https://prng.di.unimi.it/ */
-    const uint64_t result = rotl(secp256k1_test_state[0] + secp256k1_test_state[3], 23) + secp256k1_test_state[0];
-    const uint64_t t = secp256k1_test_state[1] << 17;
-    secp256k1_test_state[2] ^= secp256k1_test_state[0];
-    secp256k1_test_state[3] ^= secp256k1_test_state[1];
-    secp256k1_test_state[1] ^= secp256k1_test_state[2];
-    secp256k1_test_state[0] ^= secp256k1_test_state[3];
-    secp256k1_test_state[2] ^= t;
-    secp256k1_test_state[3] = rotl(secp256k1_test_state[3], 45);
-    return result;
+SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) {
+    if (secp256k1_test_rng_precomputed_used == 8) {
+        secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed));
+        secp256k1_test_rng_precomputed_used = 0;
+    }
+    return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++];
 }
 
-SECP256K1_INLINE static uint64_t secp256k1_testrand_bits(int bits) {
-    uint64_t ret;
+static uint32_t secp256k1_testrand_bits(int bits) {
+    uint32_t ret;
     if (secp256k1_test_rng_integer_bits_left < bits) {
-        secp256k1_test_rng_integer = secp256k1_testrand64();
-        secp256k1_test_rng_integer_bits_left = 64;
+        secp256k1_test_rng_integer |= (((uint64_t)secp256k1_testrand32()) << secp256k1_test_rng_integer_bits_left);
+        secp256k1_test_rng_integer_bits_left += 32;
     }
     ret = secp256k1_test_rng_integer;
     secp256k1_test_rng_integer >>= bits;
     secp256k1_test_rng_integer_bits_left -= bits;
-    ret &= ((~((uint64_t)0)) >> (64 - bits));
+    ret &= ((~((uint32_t)0)) >> (32 - bits));
     return ret;
 }
 
-SECP256K1_INLINE static uint32_t secp256k1_testrand32(void) {
-    return secp256k1_testrand_bits(32);
-}
-
 static uint32_t secp256k1_testrand_int(uint32_t range) {
     /* We want a uniform integer between 0 and range-1, inclusive.
      * B is the smallest number such that range <= 2**B.
@@ -112,19 +85,7 @@ static uint32_t secp256k1_testrand_int(uint32_t range) {
 }
 
 static void secp256k1_testrand256(unsigned char *b32) {
-    int i;
-    for (i = 0; i < 4; ++i) {
-        uint64_t val = secp256k1_testrand64();
-        b32[0] = val;
-        b32[1] = val >> 8;
-        b32[2] = val >> 16;
-        b32[3] = val >> 24;
-        b32[4] = val >> 32;
-        b32[5] = val >> 40;
-        b32[6] = val >> 48;
-        b32[7] = val >> 56;
-        b32 += 8;
-    }
+    secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32);
 }
 
 static void secp256k1_testrand_bytes_test(unsigned char *bytes, size_t len) {
@@ -148,7 +109,7 @@ static void secp256k1_testrand256_test(unsigned char *b32) {
 }
 
 static void secp256k1_testrand_flip(unsigned char *b, size_t len) {
-    b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_bits(3));
+    b[secp256k1_testrand_int(len)] ^= (1 << secp256k1_testrand_int(8));
 }
 
 static void secp256k1_testrand_init(const char* hexseed) {
diff --git a/src/tests.c b/src/tests.c
index a747499..e71433b 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -28,6 +28,8 @@
 #include "modinv64_impl.h"
 #endif
 
+#define CONDITIONAL_TEST(cnt, nam) if (count < (cnt)) { printf("Skipping %s (iteration count too low)\n", nam); } else
+
 static int count = 64;
 static secp256k1_context *ctx = NULL;
 
@@ -100,12 +102,6 @@ void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *
     gej->infinity = ge->infinity;
 }
 
-void random_gej_test(secp256k1_gej *gej) {
-    secp256k1_ge ge;
-    random_group_element_test(&ge);
-    random_group_element_jacobian_test(gej, &ge);
-}
-
 void random_scalar_order_test(secp256k1_scalar *num) {
     do {
         unsigned char b32[32];
@@ -796,7 +792,7 @@ void signed30_to_uint16(uint16_t* out, const secp256k1_modinv32_signed30* in) {
 void mutate_sign_signed30(secp256k1_modinv32_signed30* x) {
     int i;
     for (i = 0; i < 16; ++i) {
-        int pos = secp256k1_testrand_bits(3);
+        int pos = secp256k1_testrand_int(8);
         if (x->v[pos] > 0 && x->v[pos + 1] <= 0x3fffffff) {
             x->v[pos] -= 0x40000000;
             x->v[pos + 1] += 1;
@@ -868,7 +864,7 @@ void mutate_sign_signed62(secp256k1_modinv64_signed62* x) {
     static const int64_t M62 = (int64_t)(UINT64_MAX >> 2);
     int i;
     for (i = 0; i < 8; ++i) {
-        int pos = secp256k1_testrand_bits(2);
+        int pos = secp256k1_testrand_int(4);
         if (x->v[pos] > 0 && x->v[pos + 1] <= M62) {
             x->v[pos] -= (M62 + 1);
             x->v[pos + 1] += 1;
@@ -2457,16 +2453,13 @@ void run_field_convert(void) {
     CHECK(secp256k1_memcmp_var(&fes2, &fes, sizeof(fes)) == 0);
 }
 
-/* Returns true if two field elements have the same representation. */
-int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) {
-    int ret = 1;
+int fe_secp256k1_memcmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
+    secp256k1_fe t = *b;
 #ifdef VERIFY
-    ret &= (a->magnitude == b->magnitude);
-    ret &= (a->normalized == b->normalized);
+    t.magnitude = a->magnitude;
+    t.normalized = a->normalized;
 #endif
-    /* Compare the struct member that holds the limbs. */
-    ret &= (secp256k1_memcmp_var(a->n, b->n, sizeof(a->n)) == 0);
-    return ret;
+    return secp256k1_memcmp_var(a, &t, sizeof(secp256k1_fe));
 }
 
 void run_field_misc(void) {
@@ -2492,13 +2485,13 @@ void run_field_misc(void) {
         CHECK(x.normalized && x.magnitude == 1);
 #endif
         secp256k1_fe_cmov(&x, &x, 1);
-        CHECK(!fe_identical(&x, &z));
-        CHECK(fe_identical(&x, &q));
+        CHECK(fe_secp256k1_memcmp_var(&x, &z) != 0);
+        CHECK(fe_secp256k1_memcmp_var(&x, &q) == 0);
         secp256k1_fe_cmov(&q, &z, 1);
 #ifdef VERIFY
         CHECK(!q.normalized && q.magnitude == z.magnitude);
 #endif
-        CHECK(fe_identical(&q, &z));
+        CHECK(fe_secp256k1_memcmp_var(&q, &z) == 0);
         secp256k1_fe_normalize_var(&x);
         secp256k1_fe_normalize_var(&z);
         CHECK(!secp256k1_fe_equal_var(&x, &z));
@@ -3347,37 +3340,6 @@ void run_ge(void) {
     test_intialized_inf();
 }
 
-void test_gej_cmov(const secp256k1_gej *a, const secp256k1_gej *b) {
-    secp256k1_gej t = *a;
-    secp256k1_gej_cmov(&t, b, 0);
-    CHECK(gej_xyz_equals_gej(&t, a));
-    secp256k1_gej_cmov(&t, b, 1);
-    CHECK(gej_xyz_equals_gej(&t, b));
-}
-
-void run_gej(void) {
-    int i;
-    secp256k1_gej a, b;
-
-    /* Tests for secp256k1_gej_cmov */
-    for (i = 0; i < count; i++) {
-        secp256k1_gej_set_infinity(&a);
-        secp256k1_gej_set_infinity(&b);
-        test_gej_cmov(&a, &b);
-
-        random_gej_test(&a);
-        test_gej_cmov(&a, &b);
-        test_gej_cmov(&b, &a);
-
-        b = a;
-        test_gej_cmov(&a, &b);
-
-        random_gej_test(&b);
-        test_gej_cmov(&a, &b);
-        test_gej_cmov(&b, &a);
-    }
-}
-
 void test_ec_combine(void) {
     secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0);
     secp256k1_pubkey data[6];
@@ -4301,7 +4263,7 @@ void test_secp256k1_pippenger_bucket_window_inv(void) {
  * for a given scratch space.
  */
 void test_ecmult_multi_pippenger_max_points(void) {
-    size_t scratch_size = secp256k1_testrand_bits(8);
+    size_t scratch_size = secp256k1_testrand_int(256);
     size_t max_size = secp256k1_pippenger_scratch_size(secp256k1_pippenger_bucket_window_inv(PIPPENGER_MAX_BUCKET_WINDOW-1)+512, 12);
     secp256k1_scratch *scratch;
     size_t n_points_supported;
@@ -4559,7 +4521,7 @@ void test_constant_wnaf(const secp256k1_scalar *number, int w) {
         secp256k1_scalar_add(&x, &x, &t);
     }
     /* Skew num because when encoding numbers as odd we use an offset */
-    secp256k1_scalar_set_int(&scalar_skew, skew);
+    secp256k1_scalar_set_int(&scalar_skew, 1 << (skew == 2));
     secp256k1_scalar_add(&num, &num, &scalar_skew);
     CHECK(secp256k1_scalar_eq(&x, &num));
 }
@@ -4752,8 +4714,8 @@ void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, se
     }
 }
 
-void test_ecmult_constants(void) {
-    /* Test ecmult_gen for:
+void test_ecmult_constants_2bit(void) {
+    /* Using test_ecmult_accumulate, test ecmult for:
      * - For i in 0..36:
      *   - Key i
      *   - Key -i
@@ -4796,8 +4758,81 @@ void test_ecmult_constants(void) {
     secp256k1_scratch_space_destroy(ctx, scratch);
 }
 
+void test_ecmult_constants_sha(uint32_t prefix, size_t iter, const unsigned char* expected32) {
+    /* Using test_ecmult_accumulate, test ecmult for:
+     * - Key 0
+     * - Key 1
+     * - Key -1
+     * - For i in range(iter):
+     *   - Key SHA256(LE32(prefix) || LE16(i))
+     */
+    secp256k1_scalar x;
+    secp256k1_sha256 acc;
+    unsigned char b32[32];
+    unsigned char inp[6];
+    size_t i;
+    secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 65536);
+
+    inp[0] = prefix & 0xFF;
+    inp[1] = (prefix >> 8) & 0xFF;
+    inp[2] = (prefix >> 16) & 0xFF;
+    inp[3] = (prefix >> 24) & 0xFF;
+    secp256k1_sha256_initialize(&acc);
+    secp256k1_scalar_set_int(&x, 0);
+    test_ecmult_accumulate(&acc, &x, scratch);
+    secp256k1_scalar_set_int(&x, 1);
+    test_ecmult_accumulate(&acc, &x, scratch);
+    secp256k1_scalar_negate(&x, &x);
+    test_ecmult_accumulate(&acc, &x, scratch);
+
+    for (i = 0; i < iter; ++i) {
+        secp256k1_sha256 gen;
+        inp[4] = i & 0xff;
+        inp[5] = (i >> 8) & 0xff;
+        secp256k1_sha256_initialize(&gen);
+        secp256k1_sha256_write(&gen, inp, sizeof(inp));
+        secp256k1_sha256_finalize(&gen, b32);
+        secp256k1_scalar_set_b32(&x, b32, NULL);
+        test_ecmult_accumulate(&acc, &x, scratch);
+    }
+    secp256k1_sha256_finalize(&acc, b32);
+    CHECK(secp256k1_memcmp_var(b32, expected32, 32) == 0);
+
+    secp256k1_scratch_space_destroy(ctx, scratch);
+}
+
 void run_ecmult_constants(void) {
-    test_ecmult_constants();
+    /* Expected hashes of all points in the tests below. Computed using an
+     * independent implementation. */
+    static const unsigned char expected32_6bit20[32] = {
+        0x68, 0xb6, 0xed, 0x6f, 0x28, 0xca, 0xc9, 0x7f,
+        0x8e, 0x8b, 0xd6, 0xc0, 0x61, 0x79, 0x34, 0x6e,
+        0x5a, 0x8f, 0x2b, 0xbc, 0x3e, 0x1f, 0xc5, 0x2e,
+        0x2a, 0xd0, 0x45, 0x67, 0x7f, 0x95, 0x95, 0x8e
+    };
+    static const unsigned char expected32_8bit8[32] = {
+        0x8b, 0x65, 0x8e, 0xea, 0x86, 0xae, 0x3c, 0x95,
+        0x90, 0xb6, 0x77, 0xa4, 0x8c, 0x76, 0xd9, 0xec,
+        0xf5, 0xab, 0x8a, 0x2f, 0xfd, 0xdb, 0x19, 0x12,
+        0x1a, 0xee, 0xe6, 0xb7, 0x6e, 0x05, 0x3f, 0xc6
+    };
+    /* For every combination of 6 bit positions out of 256, restricted to
+     * 20-bit windows (i.e., the first and last bit position are no more than
+     * 19 bits apart), all 64 bit patterns occur in the input scalars used in
+     * this test. */
+    CONDITIONAL_TEST(1, "test_ecmult_constants_sha 1024") {
+        test_ecmult_constants_sha(4808378u, 1024, expected32_6bit20);
+    }
+
+    /* For every combination of 8 consecutive bit positions, all 256 bit
+     * patterns occur in the input scalars used in this test. */
+    CONDITIONAL_TEST(3, "test_ecmult_constants_sha 2048") {
+        test_ecmult_constants_sha(1607366309u, 2048, expected32_8bit8);
+    }
+
+    CONDITIONAL_TEST(35, "test_ecmult_constants_2bit") {
+        test_ecmult_constants_2bit();
+    }
 }
 
 void test_ecmult_gen_blind(void) {
@@ -6063,14 +6098,14 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
         /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */
         nlow[n] = der ? 1 : (secp256k1_testrand_bits(3) != 0);
         /* The length of the number in bytes (the first byte of which will always be nonzero) */
-        nlen[n] = nlow[n] ? secp256k1_testrand_int(33) : 32 + secp256k1_testrand_int(200) * secp256k1_testrand_bits(3) / 8;
+        nlen[n] = nlow[n] ? secp256k1_testrand_int(33) : 32 + secp256k1_testrand_int(200) * secp256k1_testrand_int(8) / 8;
         CHECK(nlen[n] <= 232);
         /* The top bit of the number. */
         nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_testrand_bits(1));
         /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */
         nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_testrand_bits(7) : 1 + secp256k1_testrand_int(127));
         /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */
-        nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_testrand_int(3) : secp256k1_testrand_int(300 - nlen[n]) * secp256k1_testrand_bits(3) / 8);
+        nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_testrand_int(3) : secp256k1_testrand_int(300 - nlen[n]) * secp256k1_testrand_int(8) / 8);
         if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) {
             *certainly_not_der = 1;
         }
@@ -6079,7 +6114,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
         nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2);
         if (!der) {
             /* nlenlen[n] max 127 bytes */
-            int add = secp256k1_testrand_int(127 - nlenlen[n]) * secp256k1_testrand_bits(4) * secp256k1_testrand_bits(4) / 256;
+            int add = secp256k1_testrand_int(127 - nlenlen[n]) * secp256k1_testrand_int(16) * secp256k1_testrand_int(16) / 256;
             nlenlen[n] += add;
             if (add != 0) {
                 *certainly_not_der = 1;
@@ -6093,7 +6128,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
     CHECK(tlen <= 856);
 
     /* The length of the garbage inside the tuple. */
-    elen = (der || indet) ? 0 : secp256k1_testrand_int(980 - tlen) * secp256k1_testrand_bits(3) / 8;
+    elen = (der || indet) ? 0 : secp256k1_testrand_int(980 - tlen) * secp256k1_testrand_int(8) / 8;
     if (elen != 0) {
         *certainly_not_der = 1;
     }
@@ -6101,7 +6136,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
     CHECK(tlen <= 980);
 
     /* The length of the garbage after the end of the tuple. */
-    glen = der ? 0 : secp256k1_testrand_int(990 - tlen) * secp256k1_testrand_bits(3) / 8;
+    glen = der ? 0 : secp256k1_testrand_int(990 - tlen) * secp256k1_testrand_int(8) / 8;
     if (glen != 0) {
         *certainly_not_der = 1;
     }
@@ -6116,7 +6151,7 @@ static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly
     } else {
         int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2);
         if (!der) {
-            int add = secp256k1_testrand_int(127 - tlenlen) * secp256k1_testrand_bits(4) * secp256k1_testrand_bits(4) / 256;
+            int add = secp256k1_testrand_int(127 - tlenlen) * secp256k1_testrand_int(16) * secp256k1_testrand_int(16) / 256;
             tlenlen += add;
             if (add != 0) {
                 *certainly_not_der = 1;
@@ -6845,7 +6880,6 @@ int main(int argc, char **argv) {
 
     /* group tests */
     run_ge();
-    run_gej();
     run_group_decompress();
 
     /* ecmult tests */