Repository: bitcoin-core/secp256k1
Pull request #748 (2020-04-30 12:12:37)

Add usage examples
Resolves https://github.com/bitcoin-core/secp256k1/issues/184

I didn't add a sign/verify/recover example yet because I'd prefer that the example included hashing a message, 
but we don't provide a hash function so I decided to start only with key generation and ECDH, and get feedback.

I *did not* check the example on windows, but did try to implement it correctly, if anyone has access to a windows machine(and knows how to build on windows hehe) please test this :)

P.S. Should the usage examples be MIT or CC0(Public Domain)?

elichai: > Alternatively the module-specific examples could be in their respective module directory. But I think either way is fine.

I thought about it, but I think that for newcomers it's easier to see an examples directory at the top level and start from there.

real-or-random: My feeling is that I think this entire issue of secret key handling deserves a broader discussion.
jonasnick: Would be cool to revive this PR since it's really helpful, well-documented and provides best practices for getting randomness on different platforms. In particular, I don't think this PR should be blocked on the result of the key generation discussion. @elichai if you want to add examples for the schnorrsig module, feel free to do it, but we can also add this in a different PR.
elichai: Rebased, and replaced the `keygen` example with a schnorr and ecdsa examples.
Please review and comment if you think something I've done isn't best practice or some comment's wording isn't good enough
real-or-random: > P.S. Should the usage examples be MIT or CC0(Public Domain)?

I think we should make them CC0 to avoid any doubt.

elichai: Rebased and fixed review comments,
Feel free to suggest alternative phrasings to the comments I wrote (English is my 2nd language)
elichai: Rebasing on master again so the CI will work
elichai: Added a commit fixing some mingw linking bugs:
1. Need to include `windows.h` and `ntstatus.h` even though they're used in `bcrypt.h` (https://sourceforge.net/p/mingw-w64/bugs/903/)
2. Had to explicitly link to `libbcrypt`
elichai: I fixed the comments in a separate commit.
I'll squash them closer to merging as I assume the Review Club will give me a bunch more things to fix.
real-or-random: We spend a lot of time here coming up with methods to obtain randomness on various OSes. If we're going to maintain this in this example file, could we just go ahead and maintain a real function? (I don't want to suggest a "yes" or "no", I'm really asking this as an open question.) I assume the overhead of maintaining a function is larger but I'm not sure to what extend.
jesseposner: Tested ACK 4c433823a85cac975b0746203d94ce041c10299d
Emzy: Tested ACK 4c43382 (Ubuntu 20.04.3 LTS)
scgbckbone: Tested ACK
* ubuntu 20.04 `Linux workstation 5.13.0-28-generic #31~20.04.1-Ubuntu SMP Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux`
* windows 10 `MINGW64_NT-10.0-19044 DESKTOP-UV75MOO 3.3.3-341.x86_64 2022-01-18 13:00 UTC x86_64 Msys`
elichai: Fixed comments, and squashed

Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index a352406..8e18c6e 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -23,6 +23,8 @@ env:
   BENCH: yes
   SECP256K1_BENCH_ITERS: 2
   CTIMETEST: yes
+  # Compile and run the tests
+  EXAMPLES: yes
 
 cat_logs_snippet: &CAT_LOGS
   always:
@@ -135,8 +137,7 @@ task:
   ##   - rm /tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
   ##
   brew_valgrind_pre_script:
-    # Retry a few times because this tends to fail randomly.
-    - for i in {1..5}; do brew update && break || sleep 15; done
+    - brew update
     - brew config
     - brew tap LouisBrunner/valgrind
     # Fetch valgrind source but don't build it yet.
@@ -323,10 +324,3 @@ task:
   test_script:
     - ./ci/cirrus.sh
   << : *CAT_LOGS
-
-task:
-  name: "sage prover"
-  << : *LINUX_CONTAINER
-  test_script:
-    - cd sage
-    - sage prove_group_implementations.sage
diff --git a/.gitignore b/.gitignore
index cb52998..53941f2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,11 +6,16 @@ exhaustive_tests
 precompute_ecmult_gen
 precompute_ecmult
 valgrind_ctime_test
+ecdh_example
+ecdsa_example
+schnorr_example
 *.exe
 *.so
 *.a
 *.csv
 !.gitignore
+*.log
+*.trs
 
 Makefile
 configure
diff --git a/Makefile.am b/Makefile.am
index f878529..51c5960 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -63,6 +63,7 @@ noinst_HEADERS += contrib/lax_der_parsing.h
 noinst_HEADERS += contrib/lax_der_parsing.c
 noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
 noinst_HEADERS += contrib/lax_der_privatekey_parsing.c
+noinst_HEADERS += examples/random.h
 
 PRECOMPUTED_LIB = libsecp256k1_precomputed.la
 noinst_LTLIBRARIES = $(PRECOMPUTED_LIB)
@@ -139,6 +140,40 @@ exhaustive_tests_LDFLAGS = -static
 TESTS += exhaustive_tests
 endif
 
+if USE_EXAMPLES
+noinst_PROGRAMS += ecdsa_example
+ecdsa_example_SOURCES = examples/ecdsa.c
+ecdsa_example_CPPFLAGS = -I$(top_srcdir)/include
+ecdsa_example_LDADD = libsecp256k1.la
+ecdsa_example_LDFLAGS = -static
+if BUILD_WINDOWS
+ecdsa_example_LDFLAGS += -lbcrypt
+endif
+TESTS += ecdsa_example
+if ENABLE_MODULE_ECDH
+noinst_PROGRAMS += ecdh_example
+ecdh_example_SOURCES = examples/ecdh.c
+ecdh_example_CPPFLAGS = -I$(top_srcdir)/include
+ecdh_example_LDADD = libsecp256k1.la
+ecdh_example_LDFLAGS = -static
+if BUILD_WINDOWS
+ecdh_example_LDFLAGS += -lbcrypt
+endif
+TESTS += ecdh_example
+endif
+if ENABLE_MODULE_SCHNORRSIG
+noinst_PROGRAMS += schnorr_example
+schnorr_example_SOURCES = examples/schnorr.c
+schnorr_example_CPPFLAGS = -I$(top_srcdir)/include
+schnorr_example_LDADD = libsecp256k1.la
+schnorr_example_LDFLAGS = -static
+if BUILD_WINDOWS
+schnorr_example_LDFLAGS += -lbcrypt
+endif
+TESTS += schnorr_example
+endif
+endif
+
 ### Precomputed tables
 EXTRA_PROGRAMS = precompute_ecmult precompute_ecmult_gen
 CLEANFILES = $(EXTRA_PROGRAMS)
diff --git a/README.md b/README.md
index 5fc07dd..f1ae215 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,14 @@ libsecp256k1 is built using autotools:
     $ make check  # run the test suite
     $ sudo make install  # optional
 
+Usage examples
+-----------
+  Usage examples can be found in the [examples](examples) directory. To compile them you need to configure with `--enable-examples`.
+  For experimental modules, you will also need `--enable-experimental` as well as a flag for each individual module, e.g. `--enable-module-ecdh`.
+  * [ECDSA example](examples/ecdsa.c)
+  * [Schnorr Signatures example](examples/schnorr.c)
+  * [Deriving a shared secret(ECDH) example](examples/ecdh.c)
+
 Test coverage
 -----------
 
diff --git a/ci/cirrus.sh b/ci/cirrus.sh
index 4379e25..b85f012 100755
--- a/ci/cirrus.sh
+++ b/ci/cirrus.sh
@@ -19,6 +19,7 @@ valgrind --version || true
     --with-ecmult-gen-precision="$ECMULTGENPRECISION" \
     --enable-module-ecdh="$ECDH" --enable-module-recovery="$RECOVERY" \
     --enable-module-schnorrsig="$SCHNORRSIG" \
+    --enable-examples="$EXAMPLES" \
     --with-valgrind="$WITH_VALGRIND" \
     --host="$HOST" $EXTRAFLAGS
 
diff --git a/ci/linux-debian.Dockerfile b/ci/linux-debian.Dockerfile
index 5cccbb5..fdba12a 100644
--- a/ci/linux-debian.Dockerfile
+++ b/ci/linux-debian.Dockerfile
@@ -19,8 +19,7 @@ RUN apt-get install --no-install-recommends --no-upgrade -y \
         gcc-arm-linux-gnueabihf libc6-dev-armhf-cross libc6-dbg:armhf \
         gcc-aarch64-linux-gnu libc6-dev-arm64-cross libc6-dbg:arm64 \
         gcc-powerpc64le-linux-gnu libc6-dev-ppc64el-cross libc6-dbg:ppc64el \
-        wine gcc-mingw-w64-x86-64 \
-        sagemath
+        wine gcc-mingw-w64-x86-64
 
 # Run a dummy command in wine to make it set up configuration
 RUN wine64-stable xcopy || true
diff --git a/configure.ac b/configure.ac
index 6f1cd62..4a78b88 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,12 +38,15 @@ AC_PATH_TOOL(AR, ar)
 AC_PATH_TOOL(RANLIB, ranlib)
 AC_PATH_TOOL(STRIP, strip)
 
-AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CC_C89
 if test x"$ac_cv_prog_cc_c89" = x"no"; then
   AC_MSG_ERROR([c89 compiler support required])
 fi
 AM_PROG_AS
 
+build_windows=no
+
 case $host_os in
   *darwin*)
      if  test x$cross_compiling != xyes; then
@@ -67,6 +70,9 @@ case $host_os in
        fi
      fi
    ;;
+   cygwin*|mingw*)
+     build_windows=yes
+   ;;
 esac
 
 # Try if some desirable compiler flags are supported and append them to SECP_CFLAGS.
@@ -134,6 +140,11 @@ AC_ARG_ENABLE(exhaustive_tests,
     [use_exhaustive_tests=$enableval],
     [use_exhaustive_tests=yes])
 
+AC_ARG_ENABLE(examples,
+    AS_HELP_STRING([--enable-examples],[compile the examples [default=no]]),
+    [use_examples=$enableval],
+    [use_examples=no])
+
 AC_ARG_ENABLE(module_ecdh,
     AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation]),
     [enable_module_ecdh=$enableval],
@@ -392,6 +403,7 @@ AC_SUBST(SECP_CFLAGS)
 AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"])
 AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"])
 AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"])
+AM_CONDITIONAL([USE_EXAMPLES], [test x"$use_examples" != x"no"])
 AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"])
 AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"])
 AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"])
@@ -399,6 +411,7 @@ 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"])
+AM_CONDITIONAL([BUILD_WINDOWS], [test "$build_windows" = "yes"])
 AC_SUBST(LIB_VERSION_CURRENT, _LIB_VERSION_CURRENT)
 AC_SUBST(LIB_VERSION_REVISION, _LIB_VERSION_REVISION)
 AC_SUBST(LIB_VERSION_AGE, _LIB_VERSION_AGE)
@@ -416,6 +429,7 @@ echo "  with external callbacks = $use_external_default_callbacks"
 echo "  with benchmarks         = $use_benchmark"
 echo "  with tests              = $use_tests"
 echo "  with coverage           = $enable_coverage"
+echo "  with examples           = $use_examples"
 echo "  module ecdh             = $enable_module_ecdh"
 echo "  module recovery         = $enable_module_recovery"
 echo "  module extrakeys        = $enable_module_extrakeys"
diff --git a/examples/EXAMPLES_COPYING b/examples/EXAMPLES_COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/examples/EXAMPLES_COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/examples/ecdh.c b/examples/ecdh.c
new file mode 100644
index 0000000..d7e8add
--- /dev/null
+++ b/examples/ecdh.c
@@ -0,0 +1,127 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel                                *
+ * To the extent possible under law, the author(s) have dedicated all    *
+ * copyright and related and neighboring rights to the software in this  *
+ * file to the public domain worldwide. This software is distributed     *
+ * without any warranty. For the CC0 Public Domain Dedication, see       *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+#include <secp256k1_ecdh.h>
+
+#include "random.h"
+
+
+int main(void) {
+    unsigned char seckey1[32];
+    unsigned char seckey2[32];
+    unsigned char compressed_pubkey1[33];
+    unsigned char compressed_pubkey2[33];
+    unsigned char shared_secret1[32];
+    unsigned char shared_secret2[32];
+    unsigned char randomize[32];
+    int return_val;
+    size_t len;
+    secp256k1_pubkey pubkey1;
+    secp256k1_pubkey pubkey2;
+
+    /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create`
+     * needs a context object initialized for signing, which is why we create
+     * a context with the SECP256K1_CONTEXT_SIGN flag.
+     * (The docs for `secp256k1_ecdh` don't require any special context, just
+     * some initialized context) */
+    secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+    if (!fill_random(randomize, sizeof(randomize))) {
+        printf("Failed to generate randomness\n");
+        return 1;
+    }
+    /* Randomizing the context is recommended to protect against side-channel
+     * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+     * information about it. This should never fail. */
+    return_val = secp256k1_context_randomize(ctx, randomize);
+    assert(return_val);
+
+    /*** Key Generation ***/
+
+    /* If the secret key is zero or out of range (bigger than secp256k1's
+     * order), we try to sample a new key. Note that the probability of this
+     * happening is negligible. */
+    while (1) {
+        if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
+            printf("Failed to generate randomness\n");
+            return 1;
+        }
+        if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) {
+            break;
+        }
+    }
+
+    /* Public key creation using a valid context with a verified secret key should never fail */
+    return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1);
+    assert(return_val);
+    return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2);
+    assert(return_val);
+
+    /* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
+    len = sizeof(compressed_pubkey1);
+    return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED);
+    assert(return_val);
+    /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+    assert(len == sizeof(compressed_pubkey1));
+
+    /* Serialize pubkey2 in a compressed form (33 bytes) */
+    len = sizeof(compressed_pubkey2);
+    return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
+    assert(return_val);
+    /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+    assert(len == sizeof(compressed_pubkey2));
+
+    /*** Creating the shared secret ***/
+
+    /* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified
+     * seckey and valid pubkey */
+    return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL);
+    assert(return_val);
+
+    /* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified
+     * seckey and valid pubkey */
+    return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL);
+    assert(return_val);
+
+    /* Both parties should end up with the same shared secret */
+    return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
+    assert(return_val == 0);
+
+    printf("Secret Key1: ");
+    print_hex(seckey1, sizeof(seckey1));
+    printf("Compressed Pubkey1: ");
+    print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
+    printf("\nSecret Key2: ");
+    print_hex(seckey2, sizeof(seckey2));
+    printf("Compressed Pubkey2: ");
+    print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
+    printf("\nShared Secret: ");
+    print_hex(shared_secret1, sizeof(shared_secret1));
+
+    /* This will clear everything from the context and free the memory */
+    secp256k1_context_destroy(ctx);
+
+    /* It's best practice to try to clear secrets from memory after using them.
+     * This is done because some bugs can allow an attacker to leak memory, for
+     * example through "out of bounds" array access (see Heartbleed), Or the OS
+     * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+     *
+     * TODO: Prevent these writes from being optimized out, as any good compiler
+     * will remove any writes that aren't used. */
+    memset(seckey1, 0, sizeof(seckey1));
+    memset(seckey2, 0, sizeof(seckey2));
+    memset(shared_secret1, 0, sizeof(shared_secret1));
+    memset(shared_secret2, 0, sizeof(shared_secret2));
+
+    return 0;
+}
diff --git a/examples/ecdsa.c b/examples/ecdsa.c
new file mode 100644
index 0000000..434c856
--- /dev/null
+++ b/examples/ecdsa.c
@@ -0,0 +1,137 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel                                *
+ * To the extent possible under law, the author(s) have dedicated all    *
+ * copyright and related and neighboring rights to the software in this  *
+ * file to the public domain worldwide. This software is distributed     *
+ * without any warranty. For the CC0 Public Domain Dedication, see       *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+
+#include "random.h"
+
+
+
+int main(void) {
+    /* Instead of signing the message directly, we must sign a 32-byte hash.
+     * Here the message is "Hello, world!" and the hash function was SHA-256.
+     * An actual implementation should just call SHA-256, but this example
+     * hardcodes the output to avoid depending on an additional library.
+     * See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */
+    unsigned char msg_hash[32] = {
+        0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
+        0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
+        0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
+        0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
+    };
+    unsigned char seckey[32];
+    unsigned char randomize[32];
+    unsigned char compressed_pubkey[33];
+    unsigned char serialized_signature[64];
+    size_t len;
+    int is_signature_valid;
+    int return_val;
+    secp256k1_pubkey pubkey;
+    secp256k1_ecdsa_signature sig;
+    /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create` needs
+     * a context object initialized for signing and `secp256k1_ecdsa_verify` needs
+     * a context initialized for verification, which is why we create a context
+     * for both signing and verification with the SECP256K1_CONTEXT_SIGN and
+     * SECP256K1_CONTEXT_VERIFY flags. */
+    secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+    if (!fill_random(randomize, sizeof(randomize))) {
+        printf("Failed to generate randomness\n");
+        return 1;
+    }
+    /* Randomizing the context is recommended to protect against side-channel
+     * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+     * information about it. This should never fail. */
+    return_val = secp256k1_context_randomize(ctx, randomize);
+    assert(return_val);
+
+    /*** Key Generation ***/
+
+    /* If the secret key is zero or out of range (bigger than secp256k1's
+     * order), we try to sample a new key. Note that the probability of this
+     * happening is negligible. */
+    while (1) {
+        if (!fill_random(seckey, sizeof(seckey))) {
+            printf("Failed to generate randomness\n");
+            return 1;
+        }
+        if (secp256k1_ec_seckey_verify(ctx, seckey)) {
+            break;
+        }
+    }
+
+    /* Public key creation using a valid context with a verified secret key should never fail */
+    return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey);
+    assert(return_val);
+
+    /* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */
+    len = sizeof(compressed_pubkey);
+    return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
+    assert(return_val);
+    /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+    assert(len == sizeof(compressed_pubkey));
+
+    /*** Signing ***/
+
+    /* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a
+     * custom nonce function, passing `NULL` will use the RFC-6979 safe default.
+     * Signing with a valid context, verified secret key
+     * and the default nonce function should never fail. */
+    return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL);
+    assert(return_val);
+
+    /* Serialize the signature in a compact form. Should always return 1
+     * according to the documentation in secp256k1.h. */
+    return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig);
+    assert(return_val);
+
+
+    /*** Verification ***/
+
+    /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */
+    if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) {
+        printf("Failed parsing the signature\n");
+        return 1;
+    }
+
+    /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
+    if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) {
+        printf("Failed parsing the public key\n");
+        return 1;
+    }
+
+    /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
+    is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
+
+    printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
+    printf("Secret Key: ");
+    print_hex(seckey, sizeof(seckey));
+    printf("Public Key: ");
+    print_hex(compressed_pubkey, sizeof(compressed_pubkey));
+    printf("Signature: ");
+    print_hex(serialized_signature, sizeof(serialized_signature));
+
+
+    /* This will clear everything from the context and free the memory */
+    secp256k1_context_destroy(ctx);
+
+    /* It's best practice to try to clear secrets from memory after using them.
+     * This is done because some bugs can allow an attacker to leak memory, for
+     * example through "out of bounds" array access (see Heartbleed), Or the OS
+     * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+     *
+     * TODO: Prevent these writes from being optimized out, as any good compiler
+     * will remove any writes that aren't used. */
+    memset(seckey, 0, sizeof(seckey));
+
+    return 0;
+}
diff --git a/examples/random.h b/examples/random.h
new file mode 100644
index 0000000..439226f
--- /dev/null
+++ b/examples/random.h
@@ -0,0 +1,73 @@
+/*************************************************************************
+ * Copyright (c) 2020-2021 Elichai Turkel                                *
+ * Distributed under the CC0 software license, see the accompanying file *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+/*
+ * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
+ * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
+ *
+ * Platform randomness sources:
+ * Linux   -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
+ * macOS   -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
+ * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
+ * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
+ * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
+ */
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <ntstatus.h>
+#include <bcrypt.h>
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/random.h>
+#elif defined(__OpenBSD__)
+#include <unistd.h>
+#else
+#error "Couldn't identify the OS"
+#endif
+
+#include <stddef.h>
+#include <limits.h>
+#include <stdio.h>
+
+
+/* Returns 1 on success, and 0 on failure. */
+static int fill_random(unsigned char* data, size_t size) {
+#if defined(_WIN32)
+    NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+    if (res != STATUS_SUCCESS || size > ULONG_MAX) {
+        return 0;
+    } else {
+        return 1;
+    }
+#elif defined(__linux__) || defined(__FreeBSD__)
+    /* If `getrandom(2)` is not available you should fallback to /dev/urandom */
+    ssize_t res = getrandom(data, size, 0);
+    if (res < 0 || (size_t)res != size ) {
+        return 0;
+    } else {
+        return 1;
+    }
+#elif defined(__APPLE__) || defined(__OpenBSD__)
+    /* If `getentropy(2)` is not available you should fallback to either
+     * `SecRandomCopyBytes` or /dev/urandom */
+    int res = getentropy(data, size);
+    if (res == 0) {
+        return 1;
+    } else {
+        return 0;
+    }
+#endif
+    return 0;
+}
+
+static void print_hex(unsigned char* data, size_t size) {
+    size_t i;
+    printf("0x");
+    for (i = 0; i < size; i++) {
+        printf("%02x", data[i]);
+    }
+    printf("\n");
+}
diff --git a/examples/schnorr.c b/examples/schnorr.c
new file mode 100644
index 0000000..a3533b6
--- /dev/null
+++ b/examples/schnorr.c
@@ -0,0 +1,136 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel                                *
+ * To the extent possible under law, the author(s) have dedicated all    *
+ * copyright and related and neighboring rights to the software in this  *
+ * file to the public domain worldwide. This software is distributed     *
+ * without any warranty. For the CC0 Public Domain Dedication, see       *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
+#include <secp256k1_schnorrsig.h>
+
+#include "random.h"
+
+int main(void) {
+    /* Instead of signing the message directly, we must sign a 32-byte hash.
+     * Here the message is "Hello, world!" and the hash function was SHA-256.
+     * An actual implementation should just call SHA-256, but this example
+     * hardcodes the output to avoid depending on an additional library. */
+    unsigned char msg_hash[32] = {
+        0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
+        0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
+        0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
+        0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
+    };
+    unsigned char seckey[32];
+    unsigned char randomize[32];
+    unsigned char auxiliary_rand[32];
+    unsigned char serialized_pubkey[32];
+    unsigned char signature[64];
+    int is_signature_valid;
+    int return_val;
+    secp256k1_xonly_pubkey pubkey;
+    secp256k1_keypair keypair;
+    /* The specification in secp256k1_extrakeys.h states that `secp256k1_keypair_create`
+     * needs a context object initialized for signing. And in secp256k1_schnorrsig.h
+     * they state that `secp256k1_schnorrsig_verify` needs a context initialized for
+     * verification, which is why we create a context for both signing and verification
+     * with the SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY flags. */
+    secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+    if (!fill_random(randomize, sizeof(randomize))) {
+        printf("Failed to generate randomness\n");
+        return 1;
+    }
+    /* Randomizing the context is recommended to protect against side-channel
+     * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+     * information about it. This should never fail. */
+    return_val = secp256k1_context_randomize(ctx, randomize);
+    assert(return_val);
+
+    /*** Key Generation ***/
+
+    /* If the secret key is zero or out of range (bigger than secp256k1's
+     * order), we try to sample a new key. Note that the probability of this
+     * happening is negligible. */
+    while (1) {
+        if (!fill_random(seckey, sizeof(seckey))) {
+            printf("Failed to generate randomness\n");
+            return 1;
+        }
+        /* Try to create a keypair with a valid context, it should only fail if
+         * the secret key is zero or out of range. */
+        if (secp256k1_keypair_create(ctx, &keypair, seckey)) {
+            break;
+        }
+    }
+
+    /* Extract the X-only public key from the keypair. We pass NULL for
+     * `pk_parity` as the parity isn't needed for signing or verification.
+     * `secp256k1_keypair_xonly_pub` supports returning the parity for
+     * other use cases such as tests or verifying Taproot tweaks.
+     * This should never fail with a valid context and public key. */
+    return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);
+    assert(return_val);
+
+    /* Serialize the public key. Should always return 1 for a valid public key. */
+    return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);
+    assert(return_val);
+
+    /*** Signing ***/
+
+    /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
+    if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
+        printf("Failed to generate randomness\n");
+        return 1;
+    }
+
+    /* Generate a Schnorr signature `noncefp` and `ndata` allows you to pass a
+     * custom nonce function, passing `NULL` will use the BIP-340 safe default.
+     * BIP-340 recommends passing 32 bytes of randomness to the nonce function to
+     * improve security against side-channel attacks. Signing with a valid
+     * context, verified keypair and the default nonce function should never
+     * fail. */
+    return_val = secp256k1_schnorrsig_sign(ctx, signature, msg_hash, &keypair, auxiliary_rand);
+    assert(return_val);
+
+    /*** Verification ***/
+
+    /* Deserialize the public key. This will return 0 if the public key can't
+     * be parsed correctly */
+    if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {
+        printf("Failed parsing the public key\n");
+        return 1;
+    }
+
+    /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
+    is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
+
+
+    printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
+    printf("Secret Key: ");
+    print_hex(seckey, sizeof(seckey));
+    printf("Public Key: ");
+    print_hex(serialized_pubkey, sizeof(serialized_pubkey));
+    printf("Signature: ");
+    print_hex(signature, sizeof(signature));
+
+    /* This will clear everything from the context and free the memory */
+    secp256k1_context_destroy(ctx);
+
+    /* It's best practice to try to clear secrets from memory after using them.
+     * This is done because some bugs can allow an attacker to leak memory, for
+     * example through "out of bounds" array access (see Heartbleed), Or the OS
+     * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+     *
+     * TODO: Prevent these writes from being optimized out, as any good compiler
+     * will remove any writes that aren't used. */
+    memset(seckey, 0, sizeof(seckey));
+
+    return 0;
+}
diff --git a/sage/group_prover.sage b/sage/group_prover.sage
index 9305c21..b200bfe 100644
--- a/sage/group_prover.sage
+++ b/sage/group_prover.sage
@@ -164,9 +164,6 @@ class constraints:
   def negate(self):
     return constraints(zero=self.nonzero, nonzero=self.zero)
 
-  def map(self, fun):
-    return constraints(zero={fun(k): v for k, v in self.zero.items()}, nonzero={fun(k): v for k, v in self.nonzero.items()})
-
   def __add__(self, other):
     zero = self.zero.copy()
     zero.update(other.zero)
@@ -180,30 +177,6 @@ class constraints:
   def __repr__(self):
     return "%s" % self
 
-def normalize_factor(p):
-  """Normalizes the sign of primitive polynomials (as returned by factor())
-
-  This function ensures that the polynomial has a positive leading coefficient.
-
-  This is necessary because recent sage versions (starting with v9.3 or v9.4,
-  we don't know) are inconsistent about the placement of the minus sign in
-  polynomial factorizations:
-  ```
-  sage: R.<ax,bx,ay,by,Az,Bz,Ai,Bi> = PolynomialRing(QQ,8,order='invlex')
-  sage: R((-2 * (bx - ax)) ^ 1).factor()
-  (-2) * (bx - ax)
-  sage: R((-2 * (bx - ax)) ^ 2).factor()
-  (4) * (-bx + ax)^2
-  sage: R((-2 * (bx - ax)) ^ 3).factor()
-  (8) * (-bx + ax)^3
-  ```
-  """
-  # Assert p is not 0 and that its non-zero coeffients are coprime.
-  # (We could just work with the primitive part p/p.content() but we want to be
-  # aware if factor() does not return a primitive part in future sage versions.)
-  assert p.content() == 1
-  # Ensure that the first non-zero coefficient is positive.
-  return p if p.lc() > 0 else -p
 
 def conflicts(R, con):
   """Check whether any of the passed non-zero assumptions is implied by the zero assumptions"""
@@ -231,10 +204,10 @@ def get_nonzero_set(R, assume):
   nonzero = set()
   for nz in map(numerator, assume.nonzero):
     for (f,n) in nz.factor():
-      nonzero.add(normalize_factor(f))
+      nonzero.add(f)
     rnz = zero.reduce(nz)
     for (f,n) in rnz.factor():
-      nonzero.add(normalize_factor(f))
+      nonzero.add(f)
   return nonzero
 
 
@@ -249,27 +222,27 @@ def prove_nonzero(R, exprs, assume):
       return (False, [exprs[expr]])
   allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1)
   for (f, n) in allexprs.factor():
-    if normalize_factor(f) not in nonzero:
+    if f not in nonzero:
       ok = False
   if ok:
     return (True, None)
   ok = True
-  for (f, n) in zero.reduce(allexprs).factor():
-    if normalize_factor(f) not in nonzero:
+  for (f, n) in zero.reduce(numerator(allexprs)).factor():
+    if f not in nonzero:
       ok = False
   if ok:
     return (True, None)
   ok = True
   for expr in exprs:
     for (f,n) in numerator(expr).factor():
-      if normalize_factor(f) not in nonzero:
+      if f not in nonzero:
         ok = False
   if ok:
     return (True, None)
   ok = True
   for expr in exprs:
     for (f,n) in zero.reduce(numerator(expr)).factor():
-      if normalize_factor(f) not in nonzero:
+      if f not in nonzero:
         expl.add(exprs[expr])
   if expl:
     return (False, list(expl))
@@ -281,7 +254,7 @@ def prove_zero(R, exprs, assume):
   """Check whether all of the passed expressions are provably zero, given assumptions"""
   r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume)
   if not r:
-    return (False, list(map(lambda x: "Possibly zero denominator: %s" % x, e)))
+    return (False, map(lambda x: "Possibly zero denominator: %s" % x, e))
   zero = R.ideal(list(map(numerator, assume.zero)))
   nonzero = prod(x for x in assume.nonzero)
   expl = []
@@ -306,8 +279,8 @@ def describe_extra(R, assume, assumeExtra):
     if base not in zero:
       add = []
       for (f, n) in numerator(base).factor():
-        if normalize_factor(f) not in nonzero:
-          add += ["%s" % normalize_factor(f)]
+        if f not in nonzero:
+          add += ["%s" % f]
       if add:
         ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base])
   # Iterate over the extra nonzero expressions
@@ -315,8 +288,8 @@ def describe_extra(R, assume, assumeExtra):
     nzr = zeroextra.reduce(numerator(nz))
     if nzr not in zeroextra:
       for (f,n) in nzr.factor():
-        if normalize_factor(zeroextra.reduce(f)) not in nonzero:
-          ret.add("%s != 0" % normalize_factor(zeroextra.reduce(f)))
+        if zeroextra.reduce(f) not in nonzero:
+          ret.add("%s != 0" % zeroextra.reduce(f))
   return ", ".join(x for x in ret)
 
 
@@ -326,21 +299,22 @@ def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require):
 
   if conflicts(R, assume):
     # This formula does not apply
-    return (True, None)
+    return None
 
   describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert)
-  if describe != "":
-    describe = " (assuming " + describe + ")"
 
   ok, msg = prove_zero(R, require.zero, assume)
   if not ok:
-    return (False, "FAIL, %s fails%s" % (str(msg), describe))
+    return "FAIL, %s fails (assuming %s)" % (str(msg), describe)
 
   res, expl = prove_nonzero(R, require.nonzero, assume)
   if not res:
-    return (False, "FAIL, %s fails%s" % (str(expl), describe))
+    return "FAIL, %s fails (assuming %s)" % (str(expl), describe)
 
-  return (True, "OK%s" % describe)
+  if describe != "":
+    return "OK (assuming %s)" % describe
+  else:
+    return "OK"
 
 
 def concrete_verify(c):
diff --git a/sage/prove_group_implementations.sage b/sage/prove_group_implementations.sage
index 96ce335..a97e732 100644
--- a/sage/prove_group_implementations.sage
+++ b/sage/prove_group_implementations.sage
@@ -8,20 +8,25 @@ load("weierstrass_prover.sage")
 def formula_secp256k1_gej_double_var(a):
   """libsecp256k1's secp256k1_gej_double_var, used by various addition functions"""
   rz = a.Z * a.Y
-  s = a.Y^2
-  l = a.X^2
-  l = l * 3
-  l = l / 2
-  t = -s
-  t = t * a.X
-  rx = l^2
-  rx = rx + t
-  rx = rx + t
-  s = s^2
-  t = t + rx
-  ry = t * l
-  ry = ry + s
-  ry = -ry
+  rz = rz * 2
+  t1 = a.X^2
+  t1 = t1 * 3
+  t2 = t1^2
+  t3 = a.Y^2
+  t3 = t3 * 2
+  t4 = t3^2
+  t4 = t4 * 2
+  t3 = t3 * a.X
+  rx = t3
+  rx = rx * 4
+  rx = -rx
+  rx = rx + t2
+  t2 = -t2
+  t3 = t3 * 6
+  t3 = t3 + t2
+  ry = t1 * t3
+  t2 = -t4
+  ry = ry + t2
   return jacobianpoint(rx, ry, rz)
 
 def formula_secp256k1_gej_add_var(branch, a, b):
@@ -192,8 +197,7 @@ def formula_secp256k1_gej_add_ge(branch, a, b):
     rr_alt = rr
     m_alt = m
   n = m_alt^2
-  q = -t
-  q = q * n
+  q = n * t
   n = n^2
   if degenerate:
     n = m
@@ -206,6 +210,8 @@ def formula_secp256k1_gej_add_ge(branch, a, b):
     zeroes.update({rz : 'r.z=0'})
   else:
     nonzeroes.update({rz : 'r.z!=0'})
+  rz = rz * 2
+  q = -q
   t = t + q
   rx = t
   t = t * 2
@@ -213,7 +219,8 @@ def formula_secp256k1_gej_add_ge(branch, a, b):
   t = t * rr_alt
   t = t + n
   ry = -t
-  ry = ry / 2
+  rx = rx * 4
+  ry = ry * 4
   if a_infinity:
     rx = b.X
     ry = b.Y
@@ -285,18 +292,15 @@ def formula_secp256k1_gej_add_ge_old(branch, a, b):
   return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz))
 
 if __name__ == "__main__":
-  success = True
-  success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
-  success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
-  success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
-  success = success & check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
-  success = success & (not check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old))
+  check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var)
+  check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var)
+  check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var)
+  check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge)
+  check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old)
 
   if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive":
-    success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
-    success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
-    success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
-    success = success & check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
-    success = success & (not check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43))
-
-  sys.exit(int(not success))
+    check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43)
+    check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43)
+    check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43)
+    check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43)
+    check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43)
diff --git a/sage/weierstrass_prover.sage b/sage/weierstrass_prover.sage
index be9cfd4..b770c6d 100644
--- a/sage/weierstrass_prover.sage
+++ b/sage/weierstrass_prover.sage
@@ -184,7 +184,6 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
       if r:
         points.append(point)
 
-  ret = True
   for za in range(1, p):
     for zb in range(1, p):
       for pa in points:
@@ -212,11 +211,8 @@ def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p):
                         match = True
                       r, e = concrete_verify(require)
                       if not r:
-                        ret = False
                         print("  failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e))
-
   print()
-  return ret
 
 
 def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC):
@@ -248,21 +244,15 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
 
   print("Formula " + name + ":")
   count = 0
-  ret = True
   for branch in range(branches):
     assumeFormula, assumeBranch, pC = formula(branch, pA, pB)
-    assumeBranch = assumeBranch.map(lift)
-    assumeFormula = assumeFormula.map(lift)
     pC.X = lift(pC.X)
     pC.Y = lift(pC.Y)
     pC.Z = lift(pC.Z)
     pC.Infinity = lift(pC.Infinity)
 
     for key in laws_jacobian_weierstrass:
-      success, msg = check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC)
-      if not success:
-        ret = False
-      res[key].append((msg, branch))
+      res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch))
 
   for key in res:
     print("  %s:" % key)
@@ -272,4 +262,3 @@ def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula):
         print("    branch %i: %s" % (x[1], x[0]))
 
   print()
-  return ret
diff --git a/src/bench_internal.c b/src/bench_internal.c
index 3c145f3..aed8216 100644
--- a/src/bench_internal.c
+++ b/src/bench_internal.c
@@ -140,15 +140,6 @@ void bench_scalar_inverse_var(void* arg, int iters) {
     CHECK(j <= iters);
 }
 
-void bench_field_half(void* arg, int iters) {
-    int i;
-    bench_inv *data = (bench_inv*)arg;
-
-    for (i = 0; i < iters; i++) {
-        secp256k1_fe_half(&data->fe[0]);
-    }
-}
-
 void bench_field_normalize(void* arg, int iters) {
     int i;
     bench_inv *data = (bench_inv*)arg;
@@ -363,7 +354,6 @@ int main(int argc, char **argv) {
     if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, iters);
     if (d || have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, iters);
 
-    if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "half")) run_benchmark("field_half", bench_field_half, bench_setup, NULL, &data, 10, iters*100);
     if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, iters*100);
     if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, iters*100);
     if (d || have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, iters*10);
diff --git a/src/ecmult_const_impl.h b/src/ecmult_const_impl.h
index 12dbcc6..3d198dc 100644
--- a/src/ecmult_const_impl.h
+++ b/src/ecmult_const_impl.h
@@ -19,10 +19,13 @@
  *  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)];
 
-    secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), pre, zr, globalz, a);
-    secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A), pre, zr);
+    /* 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 */
diff --git a/src/ecmult_impl.h b/src/ecmult_impl.h
index bbc820c..52af941 100644
--- a/src/ecmult_impl.h
+++ b/src/ecmult_impl.h
@@ -47,7 +47,7 @@
 
 /* The number of objects allocated on the scratch space for ecmult_multi algorithms */
 #define PIPPENGER_SCRATCH_OBJECTS 6
-#define STRAUSS_SCRATCH_OBJECTS 5
+#define STRAUSS_SCRATCH_OBJECTS 7
 
 #define PIPPENGER_MAX_BUCKET_WINDOW 12
 
@@ -56,23 +56,14 @@
 
 #define ECMULT_MAX_POINTS_PER_BATCH 5000000
 
-/** Fill a table 'pre_a' with precomputed odd multiples of a.
- *  pre_a will contain [1*a,3*a,...,(2*n-1)*a], so it needs space for n group elements.
- *  zr needs space for n field elements.
- *
- *  Although pre_a is an array of _ge rather than _gej, it actually represents elements
- *  in Jacobian coordinates with their z coordinates omitted. The omitted z-coordinates
- *  can be recovered using z and zr. Using the notation z(b) to represent the omitted
- *  z coordinate of b:
- *  - z(pre_a[n-1]) = 'z'
- *  - z(pre_a[i-1]) = z(pre_a[i]) / zr[i] for n > i > 0
- *
- *  Lastly the zr[0] value, which isn't used above, is set so that:
- *  - a.z = z(pre_a[0]) / zr[0]
+/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain
+ *  the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will
+ *  contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z.
+ *  Prej's Z values are undefined, except for the last value.
  */
-static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, secp256k1_fe *zr, secp256k1_fe *z, const secp256k1_gej *a) {
-    secp256k1_gej d, ai;
-    secp256k1_ge d_ge;
+static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) {
+    secp256k1_gej d;
+    secp256k1_ge a_ge, d_ge;
     int i;
 
     VERIFY_CHECK(!a->infinity);
@@ -80,74 +71,56 @@ static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_ge *pre_a, sec
     secp256k1_gej_double_var(&d, a, NULL);
 
     /*
-     * Perform the additions using an isomorphic curve Y^2 = X^3 + 7*C^6 where C := d.z.
-     * The isomorphism, phi, maps a secp256k1 point (x, y) to the point (x*C^2, y*C^3) on the other curve.
-     * In Jacobian coordinates phi maps (x, y, z) to (x*C^2, y*C^3, z) or, equivalently to (x, y, z/C).
-     *
-     *     phi(x, y, z) = (x*C^2, y*C^3, z) = (x, y, z/C)
-     *   d_ge := phi(d) = (d.x, d.y, 1)
-     *     ai := phi(a) = (a.x*C^2, a.y*C^3, a.z)
-     *
-     * The group addition functions work correctly on these isomorphic curves.
-     * In particular phi(d) is easy to represent in affine coordinates under this isomorphism.
-     * This lets us use the faster secp256k1_gej_add_ge_var group addition function that we wouldn't be able to use otherwise.
+     * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate
+     * of 'd', and scale the 1P starting value's x/y coordinates without changing its z.
      */
-    secp256k1_ge_set_xy(&d_ge, &d.x, &d.y);
-    secp256k1_ge_set_gej_zinv(&pre_a[0], a, &d.z);
-    secp256k1_gej_set_ge(&ai, &pre_a[0]);
-    ai.z = a->z;
+    d_ge.x = d.x;
+    d_ge.y = d.y;
+    d_ge.infinity = 0;
 
-    /* pre_a[0] is the point (a.x*C^2, a.y*C^3, a.z*C) which is equvalent to a.
-     * Set zr[0] to C, which is the ratio between the omitted z(pre_a[0]) value and a.z.
-     */
-    zr[0] = d.z;
+    secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z);
+    prej[0].x = a_ge.x;
+    prej[0].y = a_ge.y;
+    prej[0].z = a->z;
+    prej[0].infinity = 0;
 
+    zr[0] = d.z;
     for (i = 1; i < n; i++) {
-        secp256k1_gej_add_ge_var(&ai, &ai, &d_ge, &zr[i]);
-        secp256k1_ge_set_xy(&pre_a[i], &ai.x, &ai.y);
+        secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]);
     }
 
-    /* Multiply the last z-coordinate by C to undo the isomorphism.
-     * Since the z-coordinates of the pre_a values are implied by the zr array of z-coordinate ratios,
-     * undoing the isomorphism here undoes the isomorphism for all pre_a values.
+    /*
+     * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only
+     * the final point's z coordinate is actually used though, so just update that.
      */
-    secp256k1_fe_mul(z, &ai.z, &d.z);
+    secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z);
 }
 
-#define SECP256K1_ECMULT_TABLE_VERIFY(n,w) \
+/** 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 { \
     VERIFY_CHECK(((n) & 1) == 1); \
     VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
-    VERIFY_CHECK((n) <=  ((1 << ((w)-1)) - 1));
-
-SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge(secp256k1_ge *r, const secp256k1_ge *pre, int n, int w) {
-    SECP256K1_ECMULT_TABLE_VERIFY(n,w)
-    if (n > 0) {
-        *r = pre[(n-1)/2];
-    } else {
-        *r = pre[(-n-1)/2];
-        secp256k1_fe_negate(&(r->y), &(r->y), 1);
-    }
-}
-
-SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_lambda(secp256k1_ge *r, const secp256k1_ge *pre, const secp256k1_fe *x, int n, int w) {
-    SECP256K1_ECMULT_TABLE_VERIFY(n,w)
-    if (n > 0) {
-        secp256k1_ge_set_xy(r, &x[(n-1)/2], &pre[(n-1)/2].y);
-    } else {
-        secp256k1_ge_set_xy(r, &x[(-n-1)/2], &pre[(-n-1)/2].y);
-        secp256k1_fe_negate(&(r->y), &(r->y), 1);
-    }
-}
-
-SECP256K1_INLINE static void secp256k1_ecmult_table_get_ge_storage(secp256k1_ge *r, const secp256k1_ge_storage *pre, int n, int w) {
-    SECP256K1_ECMULT_TABLE_VERIFY(n,w)
-    if (n > 0) {
-        secp256k1_ge_from_storage(r, &pre[(n-1)/2]);
-    } else {
-        secp256k1_ge_from_storage(r, &pre[(-n-1)/2]);
-        secp256k1_fe_negate(&(r->y), &(r->y), 1);
-    }
-}
+    VERIFY_CHECK((n) <=  ((1 << ((w)-1)) - 1)); \
+    if ((n) > 0) { \
+        *(r) = (pre)[((n)-1)/2]; \
+    } else { \
+        *(r) = (pre)[(-(n)-1)/2]; \
+        secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
+    } \
+} while(0)
+
+#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \
+    VERIFY_CHECK(((n) & 1) == 1); \
+    VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \
+    VERIFY_CHECK((n) <=  ((1 << ((w)-1)) - 1)); \
+    if ((n) > 0) { \
+        secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \
+    } else { \
+        secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \
+        secp256k1_fe_negate(&((r)->y), &((r)->y), 1); \
+    } \
+} while(0)
 
 /** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits),
  *  with the following guarantees:
@@ -209,16 +182,19 @@ static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a,
 }
 
 struct secp256k1_strauss_point_state {
+    secp256k1_scalar na_1, na_lam;
     int wnaf_na_1[129];
     int wnaf_na_lam[129];
     int bits_na_1;
     int bits_na_lam;
+    size_t input_pos;
 };
 
 struct secp256k1_strauss_state {
-    /* aux is used to hold z-ratios, and then used to hold pre_a[i].x * BETA values. */
-    secp256k1_fe* aux;
+    secp256k1_gej* prej;
+    secp256k1_fe* zr;
     secp256k1_ge* pre_a;
+    secp256k1_ge* pre_a_lam;
     struct secp256k1_strauss_point_state* ps;
 };
 
@@ -236,19 +212,17 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *
     size_t np;
     size_t no = 0;
 
-    secp256k1_fe_set_int(&Z, 1);
     for (np = 0; np < num; ++np) {
-        secp256k1_gej tmp;
-        secp256k1_scalar na_1, na_lam;
         if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) {
             continue;
         }
+        state->ps[no].input_pos = np;
         /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */
-        secp256k1_scalar_split_lambda(&na_1, &na_lam, &na[np]);
+        secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]);
 
         /* build wnaf representation for na_1 and na_lam. */
-        state->ps[no].bits_na_1   = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1,   129, &na_1,   WINDOW_A);
-        state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &na_lam, WINDOW_A);
+        state->ps[no].bits_na_1   = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1,   129, &state->ps[no].na_1,   WINDOW_A);
+        state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 129, &state->ps[no].na_lam, WINDOW_A);
         VERIFY_CHECK(state->ps[no].bits_na_1 <= 129);
         VERIFY_CHECK(state->ps[no].bits_na_lam <= 129);
         if (state->ps[no].bits_na_1 > bits) {
@@ -257,36 +231,40 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *
         if (state->ps[no].bits_na_lam > bits) {
             bits = state->ps[no].bits_na_lam;
         }
+        ++no;
+    }
 
-        /* Calculate odd multiples of a.
-         * All multiples are brought to the same Z 'denominator', which is stored
-         * in Z. Due to secp256k1' isomorphism we can do all operations pretending
-         * that the Z coordinate was 1, use affine addition formulae, and correct
-         * the Z coordinate of the result once at the end.
-         * The exception is the precomputed G table points, which are actually
-         * affine. Compared to the base used for other points, they have a Z ratio
-         * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same
-         * isomorphism to efficiently add with a known Z inverse.
-         */
-        tmp = a[np];
-        if (no) {
+    /* Calculate odd multiples of a.
+     * All multiples are brought to the same Z 'denominator', which is stored
+     * in Z. Due to secp256k1' isomorphism we can do all operations pretending
+     * that the Z coordinate was 1, use affine addition formulae, and correct
+     * the Z coordinate of the result once at the end.
+     * The exception is the precomputed G table points, which are actually
+     * affine. Compared to the base used for other points, they have a Z ratio
+     * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same
+     * isomorphism to efficiently add with a known Z inverse.
+     */
+    if (no > 0) {
+        /* Compute the odd multiples in Jacobian form. */
+        secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]);
+        for (np = 1; np < no; ++np) {
+            secp256k1_gej tmp = a[state->ps[np].input_pos];
 #ifdef VERIFY
-            secp256k1_fe_normalize_var(&Z);
+            secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
 #endif
-            secp256k1_gej_rescale(&tmp, &Z);
+            secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z));
+            secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp);
+            secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z));
         }
-        secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->pre_a + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &Z, &tmp);
-        if (no) secp256k1_fe_mul(state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + no * ECMULT_TABLE_SIZE(WINDOW_A), &(a[np].z));
-
-        ++no;
+        /* Bring them to the same Z denominator. */
+        secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr);
+    } else {
+        secp256k1_fe_set_int(&Z, 1);
     }
 
-    /* Bring them to the same Z denominator. */
-    secp256k1_ge_table_set_globalz(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, state->aux);
-
     for (np = 0; np < no; ++np) {
         for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) {
-            secp256k1_fe_mul(&state->aux[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i].x, &secp256k1_const_beta);
+            secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]);
         }
     }
 
@@ -312,20 +290,20 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *
         secp256k1_gej_double_var(r, r, NULL);
         for (np = 0; np < no; ++np) {
             if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) {
-                secp256k1_ecmult_table_get_ge(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+                ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
                 secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
             }
             if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) {
-                secp256k1_ecmult_table_get_ge_lambda(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), state->aux + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
+                ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A);
                 secp256k1_gej_add_ge_var(r, r, &tmpa, NULL);
             }
         }
         if (i < bits_ng_1 && (n = wnaf_ng_1[i])) {
-            secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g, n, WINDOW_G);
+            ECMULT_TABLE_GET_GE_STORAGE(&tmpa, secp256k1_pre_g, n, WINDOW_G);
             secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
         }
         if (i < bits_ng_128 && (n = wnaf_ng_128[i])) {
-            secp256k1_ecmult_table_get_ge_storage(&tmpa, secp256k1_pre_g_128, n, WINDOW_G);
+            ECMULT_TABLE_GET_GE_STORAGE(&tmpa, secp256k1_pre_g_128, n, WINDOW_G);
             secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z);
         }
     }
@@ -336,19 +314,23 @@ static void secp256k1_ecmult_strauss_wnaf(const struct secp256k1_strauss_state *
 }
 
 static void secp256k1_ecmult(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) {
-    secp256k1_fe aux[ECMULT_TABLE_SIZE(WINDOW_A)];
+    secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)];
+    secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)];
     secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)];
     struct secp256k1_strauss_point_state ps[1];
+    secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)];
     struct secp256k1_strauss_state state;
 
-    state.aux = aux;
+    state.prej = prej;
+    state.zr = zr;
     state.pre_a = pre_a;
+    state.pre_a_lam = pre_a_lam;
     state.ps = ps;
     secp256k1_ecmult_strauss_wnaf(&state, r, 1, a, na, ng);
 }
 
 static size_t secp256k1_strauss_scratch_size(size_t n_points) {
-    static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
+    static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar);
     return n_points*point_size;
 }
 
@@ -369,11 +351,13 @@ static int secp256k1_ecmult_strauss_batch(const secp256k1_callback* error_callba
      * constant and strauss_scratch_size accordingly. */
     points = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_gej));
     scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(secp256k1_scalar));
-    state.aux = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
+    state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej));
+    state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe));
     state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
+    state.pre_a_lam = (secp256k1_ge*)secp256k1_scratch_alloc(error_callback, scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge));
     state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(error_callback, scratch, n_points * sizeof(struct secp256k1_strauss_point_state));
 
-    if (points == NULL || scalars == NULL || state.aux == NULL || state.pre_a == NULL || state.ps == NULL) {
+    if (points == NULL || scalars == NULL || state.prej == NULL || state.zr == NULL || state.pre_a == NULL || state.pre_a_lam == NULL || state.ps == NULL) {
         secp256k1_scratch_apply_checkpoint(error_callback, scratch, scratch_checkpoint);
         return 0;
     }
diff --git a/src/field.h b/src/field.h
index 2584a49..55679a2 100644
--- a/src/field.h
+++ b/src/field.h
@@ -32,12 +32,6 @@
 #error "Please select wide multiplication implementation"
 #endif
 
-static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
-static const secp256k1_fe secp256k1_const_beta = SECP256K1_FE_CONST(
-    0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul,
-    0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul
-);
-
 /** Normalize a field element. This brings the field element to a canonical representation, reduces
  *  its magnitude to 1, and reduces it modulo field size `p`.
  */
@@ -130,13 +124,4 @@ static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_f
 /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time.  Both *r and *a must be initialized.*/
 static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag);
 
-/** Halves the value of a field element modulo the field prime. Constant-time.
- *  For an input magnitude 'm', the output magnitude is set to 'floor(m/2) + 1'.
- *  The output is not guaranteed to be normalized, regardless of the input. */
-static void secp256k1_fe_half(secp256k1_fe *r);
-
-/** Sets each limb of 'r' to its upper bound at magnitude 'm'. The output will also have its
- *  magnitude set to 'm' and is normalized if (and only if) 'm' is zero. */
-static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m);
-
 #endif /* SECP256K1_FIELD_H */
diff --git a/src/field_10x26_impl.h b/src/field_10x26_impl.h
index 21742bf..aecb611 100644
--- a/src/field_10x26_impl.h
+++ b/src/field_10x26_impl.h
@@ -49,26 +49,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
 }
 #endif
 
-static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) {
-    VERIFY_CHECK(m >= 0);
-    VERIFY_CHECK(m <= 2048);
-    r->n[0] = 0x3FFFFFFUL * 2 * m;
-    r->n[1] = 0x3FFFFFFUL * 2 * m;
-    r->n[2] = 0x3FFFFFFUL * 2 * m;
-    r->n[3] = 0x3FFFFFFUL * 2 * m;
-    r->n[4] = 0x3FFFFFFUL * 2 * m;
-    r->n[5] = 0x3FFFFFFUL * 2 * m;
-    r->n[6] = 0x3FFFFFFUL * 2 * m;
-    r->n[7] = 0x3FFFFFFUL * 2 * m;
-    r->n[8] = 0x3FFFFFFUL * 2 * m;
-    r->n[9] = 0x03FFFFFUL * 2 * m;
-#ifdef VERIFY
-    r->magnitude = m;
-    r->normalized = (m == 0);
-    secp256k1_fe_verify(r);
-#endif
-}
-
 static void secp256k1_fe_normalize(secp256k1_fe *r) {
     uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
              t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
@@ -1153,82 +1133,6 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
 #endif
 }
 
-static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) {
-    uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4],
-             t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9];
-    uint32_t one = (uint32_t)1;
-    uint32_t mask = -(t0 & one) >> 6;
-
-#ifdef VERIFY
-    secp256k1_fe_verify(r);
-    VERIFY_CHECK(r->magnitude < 32);
-#endif
-
-    /* Bounds analysis (over the rationals).
-     *
-     * Let m = r->magnitude
-     *     C = 0x3FFFFFFUL * 2
-     *     D = 0x03FFFFFUL * 2
-     *
-     * Initial bounds: t0..t8 <= C * m
-     *                     t9 <= D * m
-     */
-
-    t0 += 0x3FFFC2FUL & mask;
-    t1 += 0x3FFFFBFUL & mask;
-    t2 += mask;
-    t3 += mask;
-    t4 += mask;
-    t5 += mask;
-    t6 += mask;
-    t7 += mask;
-    t8 += mask;
-    t9 += mask >> 4;
-
-    VERIFY_CHECK((t0 & one) == 0);
-
-    /* t0..t8: added <= C/2
-     *     t9: added <= D/2
-     *
-     * Current bounds: t0..t8 <= C * (m + 1/2)
-     *                     t9 <= D * (m + 1/2)
-     */
-
-    r->n[0] = (t0 >> 1) + ((t1 & one) << 25);
-    r->n[1] = (t1 >> 1) + ((t2 & one) << 25);
-    r->n[2] = (t2 >> 1) + ((t3 & one) << 25);
-    r->n[3] = (t3 >> 1) + ((t4 & one) << 25);
-    r->n[4] = (t4 >> 1) + ((t5 & one) << 25);
-    r->n[5] = (t5 >> 1) + ((t6 & one) << 25);
-    r->n[6] = (t6 >> 1) + ((t7 & one) << 25);
-    r->n[7] = (t7 >> 1) + ((t8 & one) << 25);
-    r->n[8] = (t8 >> 1) + ((t9 & one) << 25);
-    r->n[9] = (t9 >> 1);
-
-    /* t0..t8: shifted right and added <= C/4 + 1/2
-     *     t9: shifted right
-     *
-     * Current bounds: t0..t8 <= C * (m/2 + 1/2)
-     *                     t9 <= D * (m/2 + 1/4)
-     */
-
-#ifdef VERIFY
-    /* Therefore the output magnitude (M) has to be set such that:
-     *     t0..t8: C * M >= C * (m/2 + 1/2)
-     *         t9: D * M >= D * (m/2 + 1/4)
-     *
-     * It suffices for all limbs that, for any input magnitude m:
-     *     M >= m/2 + 1/2
-     *
-     * and since we want the smallest such integer value for M:
-     *     M == floor(m/2) + 1
-     */
-    r->magnitude = (r->magnitude >> 1) + 1;
-    r->normalized = 0;
-    secp256k1_fe_verify(r);
-#endif
-}
-
 static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
     uint32_t mask0, mask1;
     VG_CHECK_VERIFY(r->n, sizeof(r->n));
diff --git a/src/field_5x52_impl.h b/src/field_5x52_impl.h
index 6bd202f..9b824b9 100644
--- a/src/field_5x52_impl.h
+++ b/src/field_5x52_impl.h
@@ -58,21 +58,6 @@ static void secp256k1_fe_verify(const secp256k1_fe *a) {
 }
 #endif
 
-static void secp256k1_fe_get_bounds(secp256k1_fe *r, int m) {
-    VERIFY_CHECK(m >= 0);
-    VERIFY_CHECK(m <= 2048);
-    r->n[0] = 0xFFFFFFFFFFFFFULL * 2 * m;
-    r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * m;
-    r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * m;
-    r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * m;
-    r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * m;
-#ifdef VERIFY
-    r->magnitude = m;
-    r->normalized = (m == 0);
-    secp256k1_fe_verify(r);
-#endif
-}
-
 static void secp256k1_fe_normalize(secp256k1_fe *r) {
     uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
 
@@ -492,71 +477,6 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
 #endif
 }
 
-static SECP256K1_INLINE void secp256k1_fe_half(secp256k1_fe *r) {
-    uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4];
-    uint64_t one = (uint64_t)1;
-    uint64_t mask = -(t0 & one) >> 12;
-
-#ifdef VERIFY
-    secp256k1_fe_verify(r);
-    VERIFY_CHECK(r->magnitude < 32);
-#endif
-
-    /* Bounds analysis (over the rationals).
-     *
-     * Let m = r->magnitude
-     *     C = 0xFFFFFFFFFFFFFULL * 2
-     *     D = 0x0FFFFFFFFFFFFULL * 2
-     *
-     * Initial bounds: t0..t3 <= C * m
-     *                     t4 <= D * m
-     */
-
-    t0 += 0xFFFFEFFFFFC2FULL & mask;
-    t1 += mask;
-    t2 += mask;
-    t3 += mask;
-    t4 += mask >> 4;
-
-    VERIFY_CHECK((t0 & one) == 0);
-
-    /* t0..t3: added <= C/2
-     *     t4: added <= D/2
-     *
-     * Current bounds: t0..t3 <= C * (m + 1/2)
-     *                     t4 <= D * (m + 1/2)
-     */
-
-    r->n[0] = (t0 >> 1) + ((t1 & one) << 51);
-    r->n[1] = (t1 >> 1) + ((t2 & one) << 51);
-    r->n[2] = (t2 >> 1) + ((t3 & one) << 51);
-    r->n[3] = (t3 >> 1) + ((t4 & one) << 51);
-    r->n[4] = (t4 >> 1);
-
-    /* t0..t3: shifted right and added <= C/4 + 1/2
-     *     t4: shifted right
-     *
-     * Current bounds: t0..t3 <= C * (m/2 + 1/2)
-     *                     t4 <= D * (m/2 + 1/4)
-     */
-
-#ifdef VERIFY
-    /* Therefore the output magnitude (M) has to be set such that:
-     *     t0..t3: C * M >= C * (m/2 + 1/2)
-     *         t4: D * M >= D * (m/2 + 1/4)
-     *
-     * It suffices for all limbs that, for any input magnitude m:
-     *     M >= m/2 + 1/2
-     *
-     * and since we want the smallest such integer value for M:
-     *     M == floor(m/2) + 1
-     */
-    r->magnitude = (r->magnitude >> 1) + 1;
-    r->normalized = 0;
-    secp256k1_fe_verify(r);
-#endif
-}
-
 static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) {
     uint64_t mask0, mask1;
     VG_CHECK_VERIFY(r->n, sizeof(r->n));
diff --git a/src/field_impl.h b/src/field_impl.h
index 0a4a04d..374284a 100644
--- a/src/field_impl.h
+++ b/src/field_impl.h
@@ -135,4 +135,6 @@ static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) {
     return secp256k1_fe_equal(&t1, a);
 }
 
+static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
+
 #endif /* SECP256K1_FIELD_IMPL_H */
diff --git a/src/group.h b/src/group.h
index bb7dae1..0b55ad7 100644
--- a/src/group.h
+++ b/src/group.h
@@ -9,10 +9,7 @@
 
 #include "field.h"
 
-/** A group element in affine coordinates on the secp256k1 curve,
- *  or occasionally on an isomorphic curve of the form y^2 = x^3 + 7*t^6.
- *  Note: For exhaustive test mode, secp256k1 is replaced by a small subgroup of a different curve.
- */
+/** A group element of the secp256k1 curve, in affine coordinates. */
 typedef struct {
     secp256k1_fe x;
     secp256k1_fe y;
@@ -22,9 +19,7 @@ typedef struct {
 #define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0}
 #define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1}
 
-/** A group element of the secp256k1 curve, in jacobian coordinates.
- *  Note: For exhastive test mode, sepc256k1 is replaced by a small subgroup of a different curve.
- */
+/** A group element of the secp256k1 curve, in jacobian coordinates. */
 typedef struct {
     secp256k1_fe x; /* actual X: x/z^2 */
     secp256k1_fe y; /* actual Y: y/z^3 */
@@ -69,24 +64,12 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a);
 /** Set a batch of group elements equal to the inputs given in jacobian coordinates */
 static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len);
 
-/** Bring a batch of inputs to the same global z "denominator", based on ratios between
- *  (omitted) z coordinates of adjacent elements.
- *
- *  Although the elements a[i] are _ge rather than _gej, they actually represent elements
- *  in Jacobian coordinates with their z coordinates omitted.
- *
- *  Using the notation z(b) to represent the omitted z coordinate of b, the array zr of
- *  z coordinate ratios must satisfy zr[i] == z(a[i]) / z(a[i-1]) for 0 < 'i' < len.
- *  The zr[0] value is unused.
- *
- *  This function adjusts the coordinates of 'a' in place so that for all 'i', z(a[i]) == z(a[len-1]).
- *  In other words, the initial value of z(a[len-1]) becomes the global z "denominator". Only the
- *  a[i].x and a[i].y coordinates are explicitly modified; the adjustment of the omitted z coordinate is
- *  implicit.
- *
- *  The coordinates of the final element a[len-1] are not changed.
- */
-static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr);
+/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to
+ *  the same global z "denominator". zr must contain the known z-ratios such
+ *  that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y
+ *  coordinates of the result are stored in r, the common z coordinate is
+ *  stored in globalz. */
+static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr);
 
 /** Set a group element (affine) equal to the point at infinity. */
 static void secp256k1_ge_set_infinity(secp256k1_ge *r);
diff --git a/src/group_impl.h b/src/group_impl.h
index b19b02a..f02891d 100644
--- a/src/group_impl.h
+++ b/src/group_impl.h
@@ -161,26 +161,27 @@ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a
     }
 }
 
-static void secp256k1_ge_table_set_globalz(size_t len, secp256k1_ge *a, const secp256k1_fe *zr) {
+static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) {
     size_t i = len - 1;
     secp256k1_fe zs;
 
     if (len > 0) {
+        /* The z of the final point gives us the "global Z" for the table. */
+        r[i].x = a[i].x;
+        r[i].y = a[i].y;
         /* Ensure all y values are in weak normal form for fast negation of points */
-        secp256k1_fe_normalize_weak(&a[i].y);
+        secp256k1_fe_normalize_weak(&r[i].y);
+        *globalz = a[i].z;
+        r[i].infinity = 0;
         zs = zr[i];
 
         /* Work our way backwards, using the z-ratios to scale the x/y values. */
         while (i > 0) {
-            secp256k1_gej tmpa;
             if (i != len - 1) {
                 secp256k1_fe_mul(&zs, &zs, &zr[i]);
             }
             i--;
-            tmpa.x = a[i].x;
-            tmpa.y = a[i].y;
-            tmpa.infinity = 0;
-            secp256k1_ge_set_gej_zinv(&a[i], &tmpa, &zs);
+            secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs);
         }
     }
 }
@@ -271,35 +272,37 @@ static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) {
 }
 
 static SECP256K1_INLINE void secp256k1_gej_double(secp256k1_gej *r, const secp256k1_gej *a) {
-    /* Operations: 3 mul, 4 sqr, 8 add/half/mul_int/negate */
-    secp256k1_fe l, s, t;
+    /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate.
+     *
+     * Note that there is an implementation described at
+     *     https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
+     * which trades a multiply for a square, but in practice this is actually slower,
+     * mainly because it requires more normalizations.
+     */
+    secp256k1_fe t1,t2,t3,t4;
 
     r->infinity = a->infinity;
 
-    /* Formula used:
-     * L = (3/2) * X1^2
-     * S = Y1^2
-     * T = -X1*S
-     * X3 = L^2 + 2*T
-     * Y3 = -(L*(X3 + T) + S^2)
-     * Z3 = Y1*Z1
-     */
-
-    secp256k1_fe_mul(&r->z, &a->z, &a->y); /* Z3 = Y1*Z1 (1) */
-    secp256k1_fe_sqr(&s, &a->y);           /* S = Y1^2 (1) */
-    secp256k1_fe_sqr(&l, &a->x);           /* L = X1^2 (1) */
-    secp256k1_fe_mul_int(&l, 3);           /* L = 3*X1^2 (3) */
-    secp256k1_fe_half(&l);                 /* L = 3/2*X1^2 (2) */
-    secp256k1_fe_negate(&t, &s, 1);        /* T = -S (2) */
-    secp256k1_fe_mul(&t, &t, &a->x);       /* T = -X1*S (1) */
-    secp256k1_fe_sqr(&r->x, &l);           /* X3 = L^2 (1) */
-    secp256k1_fe_add(&r->x, &t);           /* X3 = L^2 + T (2) */
-    secp256k1_fe_add(&r->x, &t);           /* X3 = L^2 + 2*T (3) */
-    secp256k1_fe_sqr(&s, &s);              /* S' = S^2 (1) */
-    secp256k1_fe_add(&t, &r->x);           /* T' = X3 + T (4) */
-    secp256k1_fe_mul(&r->y, &t, &l);       /* Y3 = L*(X3 + T) (1) */
-    secp256k1_fe_add(&r->y, &s);           /* Y3 = L*(X3 + T) + S^2 (2) */
-    secp256k1_fe_negate(&r->y, &r->y, 2);  /* Y3 = -(L*(X3 + T) + S^2) (3) */
+    secp256k1_fe_mul(&r->z, &a->z, &a->y);
+    secp256k1_fe_mul_int(&r->z, 2);       /* Z' = 2*Y*Z (2) */
+    secp256k1_fe_sqr(&t1, &a->x);
+    secp256k1_fe_mul_int(&t1, 3);         /* T1 = 3*X^2 (3) */
+    secp256k1_fe_sqr(&t2, &t1);           /* T2 = 9*X^4 (1) */
+    secp256k1_fe_sqr(&t3, &a->y);
+    secp256k1_fe_mul_int(&t3, 2);         /* T3 = 2*Y^2 (2) */
+    secp256k1_fe_sqr(&t4, &t3);
+    secp256k1_fe_mul_int(&t4, 2);         /* T4 = 8*Y^4 (2) */
+    secp256k1_fe_mul(&t3, &t3, &a->x);    /* T3 = 2*X*Y^2 (1) */
+    r->x = t3;
+    secp256k1_fe_mul_int(&r->x, 4);       /* X' = 8*X*Y^2 (4) */
+    secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */
+    secp256k1_fe_add(&r->x, &t2);         /* X' = 9*X^4 - 8*X*Y^2 (6) */
+    secp256k1_fe_negate(&t2, &t2, 1);     /* T2 = -9*X^4 (2) */
+    secp256k1_fe_mul_int(&t3, 6);         /* T3 = 12*X*Y^2 (6) */
+    secp256k1_fe_add(&t3, &t2);           /* T3 = 12*X*Y^2 - 9*X^4 (8) */
+    secp256k1_fe_mul(&r->y, &t1, &t3);    /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */
+    secp256k1_fe_negate(&t2, &t4, 2);     /* T2 = -8*Y^4 (3) */
+    secp256k1_fe_add(&r->y, &t2);         /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */
 }
 
 static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) {
@@ -324,6 +327,7 @@ static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, s
     if (rzr != NULL) {
         *rzr = a->y;
         secp256k1_fe_normalize_weak(rzr);
+        secp256k1_fe_mul_int(rzr, 2);
     }
 
     secp256k1_gej_double(r, a);
@@ -489,7 +493,8 @@ static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a,
 
 
 static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) {
-    /* Operations: 7 mul, 5 sqr, 24 add/cmov/half/mul_int/negate/normalize_weak/normalizes_to_zero */
+    /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */
+    static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
     secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr;
     secp256k1_fe m_alt, rr_alt;
     int infinity, degenerate;
@@ -510,11 +515,11 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
      *    Z = Z1*Z2
      *    T = U1+U2
      *    M = S1+S2
-     *    Q = -T*M^2
+     *    Q = T*M^2
      *    R = T^2-U1*U2
-     *    X3 = R^2+Q
-     *    Y3 = -(R*(2*X3+Q)+M^4)/2
-     *    Z3 = M*Z
+     *    X3 = 4*(R^2-Q)
+     *    Y3 = 4*(R*(3*Q-2*R^2)-M^4)
+     *    Z3 = 2*M*Z
      *  (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.)
      *
      *  This formula has the benefit of being the same for both addition
@@ -578,8 +583,7 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
      * and denominator of lambda; R and M represent the explicit
      * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */
     secp256k1_fe_sqr(&n, &m_alt);                       /* n = Malt^2 (1) */
-    secp256k1_fe_negate(&q, &t, 2);                     /* q = -T (3) */
-    secp256k1_fe_mul(&q, &q, &n);                       /* q = Q = -T*Malt^2 (1) */
+    secp256k1_fe_mul(&q, &n, &t);                       /* q = Q = T*Malt^2 (1) */
     /* These two lines use the observation that either M == Malt or M == 0,
      * so M^3 * Malt is either Malt^4 (which is computed by squaring), or
      * zero (which is "computed" by cmov). So the cost is one squaring
@@ -587,21 +591,26 @@ static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const
     secp256k1_fe_sqr(&n, &n);
     secp256k1_fe_cmov(&n, &m, degenerate);              /* n = M^3 * Malt (2) */
     secp256k1_fe_sqr(&t, &rr_alt);                      /* t = Ralt^2 (1) */
-    secp256k1_fe_mul(&r->z, &a->z, &m_alt);             /* r->z = Z3 = Malt*Z (1) */
+    secp256k1_fe_mul(&r->z, &a->z, &m_alt);             /* r->z = Malt*Z (1) */
     infinity = secp256k1_fe_normalizes_to_zero(&r->z) & ~a->infinity;
-    secp256k1_fe_add(&t, &q);                           /* t = Ralt^2 + Q (2) */
-    r->x = t;                                           /* r->x = X3 = Ralt^2 + Q (2) */
-    secp256k1_fe_mul_int(&t, 2);                        /* t = 2*X3 (4) */
-    secp256k1_fe_add(&t, &q);                           /* t = 2*X3 + Q (5) */
-    secp256k1_fe_mul(&t, &t, &rr_alt);                  /* t = Ralt*(2*X3 + Q) (1) */
-    secp256k1_fe_add(&t, &n);                           /* t = Ralt*(2*X3 + Q) + M^3*Malt (3) */
-    secp256k1_fe_negate(&r->y, &t, 3);                  /* r->y = -(Ralt*(2*X3 + Q) + M^3*Malt) (4) */
-    secp256k1_fe_half(&r->y);                           /* r->y = Y3 = -(Ralt*(2*X3 + Q) + M^3*Malt)/2 (3) */
+    secp256k1_fe_mul_int(&r->z, 2);                     /* r->z = Z3 = 2*Malt*Z (2) */
+    secp256k1_fe_negate(&q, &q, 1);                     /* q = -Q (2) */
+    secp256k1_fe_add(&t, &q);                           /* t = Ralt^2-Q (3) */
+    secp256k1_fe_normalize_weak(&t);
+    r->x = t;                                           /* r->x = Ralt^2-Q (1) */
+    secp256k1_fe_mul_int(&t, 2);                        /* t = 2*x3 (2) */
+    secp256k1_fe_add(&t, &q);                           /* t = 2*x3 - Q: (4) */
+    secp256k1_fe_mul(&t, &t, &rr_alt);                  /* t = Ralt*(2*x3 - Q) (1) */
+    secp256k1_fe_add(&t, &n);                           /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */
+    secp256k1_fe_negate(&r->y, &t, 3);                  /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */
+    secp256k1_fe_normalize_weak(&r->y);
+    secp256k1_fe_mul_int(&r->x, 4);                     /* r->x = X3 = 4*(Ralt^2-Q) */
+    secp256k1_fe_mul_int(&r->y, 4);                     /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */
 
     /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */
     secp256k1_fe_cmov(&r->x, &b->x, a->infinity);
     secp256k1_fe_cmov(&r->y, &b->y, a->infinity);
-    secp256k1_fe_cmov(&r->z, &secp256k1_fe_one, a->infinity);
+    secp256k1_fe_cmov(&r->z, &fe_1, a->infinity);
     r->infinity = infinity;
 }
 
@@ -647,8 +656,12 @@ static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r,
 }
 
 static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) {
+    static const secp256k1_fe beta = SECP256K1_FE_CONST(
+        0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul,
+        0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul
+    );
     *r = *a;
-    secp256k1_fe_mul(&r->x, &r->x, &secp256k1_const_beta);
+    secp256k1_fe_mul(&r->x, &r->x, &beta);
 }
 
 static int secp256k1_ge_is_in_correct_subgroup(const secp256k1_ge* ge) {
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/ecdh/tests_impl.h b/src/modules/ecdh/tests_impl.h
index 10b7075..be07447 100644
--- a/src/modules/ecdh/tests_impl.h
+++ b/src/modules/ecdh/tests_impl.h
@@ -60,7 +60,7 @@ void test_ecdh_generator_basepoint(void) {
 
     s_one[31] = 1;
     /* Check against pubkey creation when the basepoint is the generator */
-    for (i = 0; i < 2 * count; ++i) {
+    for (i = 0; i < 100; ++i) {
         secp256k1_sha256 sha;
         unsigned char s_b32[32];
         unsigned char output_ecdh[65];
@@ -123,43 +123,10 @@ void test_bad_scalar(void) {
     CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow, ecdh_hash_function_test_fail, NULL) == 0);
 }
 
-/** Test that ECDH(sG, 1/s) == ECDH((1/s)G, s) == ECDH(G, 1) for a few random s. */
-void test_result_basepoint(void) {
-    secp256k1_pubkey point;
-    secp256k1_scalar rand;
-    unsigned char s[32];
-    unsigned char s_inv[32];
-    unsigned char out[32];
-    unsigned char out_inv[32];
-    unsigned char out_base[32];
-    int i;
-
-    unsigned char s_one[32] = { 0 };
-    s_one[31] = 1;
-    CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_one) == 1);
-    CHECK(secp256k1_ecdh(ctx, out_base, &point, s_one, NULL, NULL) == 1);
-
-    for (i = 0; i < 2 * count; i++) {
-        random_scalar_order(&rand);
-        secp256k1_scalar_get_b32(s, &rand);
-        secp256k1_scalar_inverse(&rand, &rand);
-        secp256k1_scalar_get_b32(s_inv, &rand);
-
-        CHECK(secp256k1_ec_pubkey_create(ctx, &point, s) == 1);
-        CHECK(secp256k1_ecdh(ctx, out, &point, s_inv, NULL, NULL) == 1);
-        CHECK(secp256k1_memcmp_var(out, out_base, 32) == 0);
-
-        CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_inv) == 1);
-        CHECK(secp256k1_ecdh(ctx, out_inv, &point, s, NULL, NULL) == 1);
-        CHECK(secp256k1_memcmp_var(out_inv, out_base, 32) == 0);
-    }
-}
-
 void run_ecdh_tests(void) {
     test_ecdh_api();
     test_ecdh_generator_basepoint();
     test_bad_scalar();
-    test_result_basepoint();
 }
 
 #endif /* SECP256K1_MODULE_ECDH_TESTS_H */
diff --git a/src/tests.c b/src/tests.c
index 26063a8..a747499 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -28,8 +28,6 @@
 #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;
 
@@ -2471,55 +2469,6 @@ int fe_identical(const secp256k1_fe *a, const secp256k1_fe *b) {
     return ret;
 }
 
-void run_field_half(void) {
-    secp256k1_fe t, u;
-    int m;
-
-    /* Check magnitude 0 input */
-    secp256k1_fe_get_bounds(&t, 0);
-    secp256k1_fe_half(&t);
-#ifdef VERIFY
-    CHECK(t.magnitude == 1);
-    CHECK(t.normalized == 0);
-#endif
-    CHECK(secp256k1_fe_normalizes_to_zero(&t));
-
-    /* Check non-zero magnitudes in the supported range */
-    for (m = 1; m < 32; m++) {
-        /* Check max-value input */
-        secp256k1_fe_get_bounds(&t, m);
-
-        u = t;
-        secp256k1_fe_half(&u);
-#ifdef VERIFY
-        CHECK(u.magnitude == (m >> 1) + 1);
-        CHECK(u.normalized == 0);
-#endif
-        secp256k1_fe_normalize_weak(&u);
-        secp256k1_fe_add(&u, &u);
-        CHECK(check_fe_equal(&t, &u));
-
-        /* Check worst-case input: ensure the LSB is 1 so that P will be added,
-         * which will also cause all carries to be 1, since all limbs that can
-         * generate a carry are initially even and all limbs of P are odd in
-         * every existing field implementation. */
-        secp256k1_fe_get_bounds(&t, m);
-        CHECK(t.n[0] > 0);
-        CHECK((t.n[0] & 1) == 0);
-        --t.n[0];
-
-        u = t;
-        secp256k1_fe_half(&u);
-#ifdef VERIFY
-        CHECK(u.magnitude == (m >> 1) + 1);
-        CHECK(u.normalized == 0);
-#endif
-        secp256k1_fe_normalize_weak(&u);
-        secp256k1_fe_add(&u, &u);
-        CHECK(check_fe_equal(&t, &u));
-    }
-}
-
 void run_field_misc(void) {
     secp256k1_fe x;
     secp256k1_fe y;
@@ -2527,13 +2476,9 @@ void run_field_misc(void) {
     secp256k1_fe q;
     secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5);
     int i, j;
-    for (i = 0; i < 1000 * count; i++) {
+    for (i = 0; i < 5*count; i++) {
         secp256k1_fe_storage xs, ys, zs;
-        if (i & 1) {
-            random_fe(&x);
-        } else {
-            random_fe_test(&x);
-        }
+        random_fe(&x);
         random_fe_non_zero(&y);
         /* Test the fe equality and comparison operations. */
         CHECK(secp256k1_fe_cmp_var(&x, &x) == 0);
@@ -2601,14 +2546,6 @@ void run_field_misc(void) {
         secp256k1_fe_add(&q, &x);
         CHECK(check_fe_equal(&y, &z));
         CHECK(check_fe_equal(&q, &y));
-        /* Check secp256k1_fe_half. */
-        z = x;
-        secp256k1_fe_half(&z);
-        secp256k1_fe_add(&z, &z);
-        CHECK(check_fe_equal(&x, &z));
-        secp256k1_fe_add(&z, &z);
-        secp256k1_fe_half(&z);
-        CHECK(check_fe_equal(&x, &z));
     }
 }
 
@@ -4815,8 +4752,8 @@ void test_ecmult_accumulate(secp256k1_sha256* acc, const secp256k1_scalar* x, se
     }
 }
 
-void test_ecmult_constants_2bit(void) {
-    /* Using test_ecmult_accumulate, test ecmult for:
+void test_ecmult_constants(void) {
+    /* Test ecmult_gen for:
      * - For i in 0..36:
      *   - Key i
      *   - Key -i
@@ -4859,81 +4796,8 @@ void test_ecmult_constants_2bit(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) {
-    /* 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();
-    }
+    test_ecmult_constants();
 }
 
 void test_ecmult_gen_blind(void) {
@@ -6973,7 +6837,6 @@ int main(int argc, char **argv) {
     run_scalar_tests();
 
     /* field tests */
-    run_field_half();
     run_field_misc();
     run_field_convert();
     run_fe_mul();