From 962e7ccf49434d49b7e67373f8963c4a8d84b8b8 Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Thu, 15 Sep 2022 09:33:34 +0200 Subject: [PATCH] CI: enable GMP tests on Windows and Linux 32-bit and fix caching (#204) * Try to compile with GMP on windows and 32-bit linux * remove leftover msys shell * Don't use GMP Mersenne Twister, bad randomness and untested Nim wrapper * properly cache nim * fix path after cache * run pacman in msys2 env * rework msys2 ... again * shell compat for file clearing * shell compat try-again for file clearing * force bash for clearing parallel builds on windows * Use nimscript directly (why didn't it work last time?) * Avoid IO redirection to support any shell * Avoid IO redirection v2 to support any shell * add debug data * add debug again * Introduce pararun, a parallel test runner to remove need of GNU parallel * pararun: style --- .github/workflows/ci.yml | 74 ++--- constantine.nimble | 287 ++++++++---------- helpers/pararun.nim | 151 +++++++++ helpers/prng_unsafe.nim | 12 + tests/math/t_bigints_mod_vs_gmp.nim | 51 ++-- .../math/t_bigints_mul_high_words_vs_gmp.nim | 53 ++-- tests/math/t_bigints_mul_vs_gmp.nim | 53 ++-- tests/math/t_finite_fields_vs_gmp.nim | 79 +++-- tests/t_hash_sha256_vs_openssl.nim | 4 +- 9 files changed, 431 insertions(+), 333 deletions(-) create mode 100644 helpers/pararun.nim diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d172fc4..0ba58d7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,10 +117,9 @@ jobs: run: | echo '${{ github.workspace }}'"/external/mingw-${{ matrix.target.cpu }}/bin" >> $GITHUB_PATH echo '${{ github.workspace }}'"/external/dlls-${{ matrix.target.cpu }}" >> $GITHUB_PATH + - name: Restore Nim from cache - if: > - steps.nim-compiler-cache.outputs.cache-hit != 'true' && - matrix.nim_version != 'devel' + if: matrix.nim_version != 'devel' id: nim-compiler-cache uses: actions/cache@v2 with: @@ -128,20 +127,28 @@ jobs: key: 'nim-${{ matrix.target.cpu }}-${{ matrix.nim_version }}' - name: Setup Nim + if: steps.nim-compiler-cache.outputs.cache-hit != 'true' uses: alaviss/setup-nim@0.1.1 with: - path: 'nim' + path: 'nim-${{ matrix.nim_version }}-${{ matrix.target.cpu }}' version: ${{ matrix.nim_version }} architecture: ${{ matrix.target.cpu }} + add-to-path: false - - name: Install dependencies (Linux amd64) + - name: Path to cached Nim + shell: bash + run: | + echo '${{ github.workspace }}'"/nim-${{ matrix.nim_version }}-${{ matrix.target.cpu }}/bin" >> $GITHUB_PATH + echo '${{ github.workspace }}'"/.nimble/bin" >> $GITHUB_PATH + + - name: Install test dependencies (Linux amd64) if: runner.os == 'Linux' && matrix.target.cpu == 'amd64' run: | sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \ --no-install-recommends -yq \ libgmp-dev - - name: Install dependencies (Linux i386) + - name: Install test dependencies (Linux i386) if: runner.os == 'Linux' && matrix.target.cpu == 'i386' run: | sudo dpkg --add-architecture i386 @@ -160,70 +167,51 @@ jobs: #!/bin/bash exec $(which g++) -m32 "\$@" EOF - chmod 755 external/bin/gcc external/bin/g++ + chmod 755 external/bin/{gcc,g++} echo '${{ github.workspace }}/external/bin' >> $GITHUB_PATH - - name: Install dependencies (macOS) + - name: Install test dependencies (macOS) if: runner.os == 'macOS' - run: brew install gmp parallel + run: brew install gmp - name: Setup MSYS2 (Windows) if: runner.os == 'Windows' uses: msys2/setup-msys2@v2 with: path-type: inherit - update: true + update: false install: base-devel git mingw-w64-x86_64-toolchain - - name: Install dependencies (Windows) + - name: Install test dependencies (Windows) if: runner.os == 'Windows' shell: msys2 {0} run: | pacman -S --needed --noconfirm mingw-w64-x86_64-gmp - pacman -S --needed --noconfirm parallel - nimble refresh -y - nimble install -y gmp stew jsony + nimble refresh --verbose -y + nimble install --verbose -y gmp stew jsony asynctools - name: Install test dependencies if: runner.os != 'Windows' shell: bash run: | - nimble refresh -y - nimble install -y gmp stew jsony + nimble refresh --verbose -y + nimble install --verbose -y gmp stew jsony asynctools - - name: Run Constantine tests (with Assembler & with GMP) - if: (runner.os == 'Linux' || runner.os == 'macOS') && matrix.target.BACKEND == 'ASM' && matrix.target.cpu != 'i386' + - name: Run Constantine tests (UNIX with Assembler) + if: runner.os != 'Windows' && matrix.target.BACKEND == 'ASM' shell: bash run: | - export UCPU="$cpu" cd constantine - nimble test_parallel - - name: Run Constantine tests (no Assembler & with GMP) - if: (runner.os == 'Linux' || runner.os == 'macOS') && matrix.target.BACKEND == 'NO_ASM' && matrix.target.cpu != 'i386' + nimble test_parallel --verbose + - name: Run Constantine tests (UNIX no Assembler) + if: runner.os != 'Windows' && matrix.target.BACKEND == 'NO_ASM' shell: bash run: | - export UCPU="$cpu" cd constantine - nimble test_parallel_no_assembler - - name: Run Constantine tests (without GMP) - if: runner.os == 'Linux' && matrix.target.BACKEND == 'ASM' && matrix.target.cpu == 'i386' - shell: bash - run: | - export UCPU="$cpu" - cd constantine - nimble test_parallel_no_gmp - - name: Run Constantine tests (without Assembler or GMP) - if: runner.os == 'Linux' && matrix.target.BACKEND == 'NO_ASM' && matrix.target.cpu == 'i386' - shell: bash - run: | - export UCPU="$cpu" - cd constantine - nimble test_parallel_no_gmp_no_assembler - - name: Run Constantine tests (Windows - without Assembler or GMP) - # TODO, why aren't GMP or parallel in path? - if: runner.os == 'Windows' + nimble test_parallel_no_asm --verbose + - name: Run Constantine tests (Windows no Assembler) + if: runner.os == 'Windows' && matrix.target.BACKEND == 'NO_ASM' shell: msys2 {0} run: | - export UCPU="$cpu" cd constantine - nimble test_no_gmp_no_assembler \ No newline at end of file + nimble test_parallel_no_asm --verbose diff --git a/constantine.nimble b/constantine.nimble index cc56572..b948498 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -21,6 +21,24 @@ const buildParallel = "test_parallel.txt" # Code refactoring requires re-enabling the full suite. # Basic primitives should stay on to catch compiler regressions. const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ + + # Hashing vs OpenSSL + # ---------------------------------------------------------- + ("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows + + # Ciphers + # ---------------------------------------------------------- + ("tests/t_cipher_chacha20.nim", false), + + # Message Authentication Code + # ---------------------------------------------------------- + ("tests/t_mac_poly1305.nim", false), + ("tests/t_mac_hmac_sha256.nim", false), + + # KDF + # ---------------------------------------------------------- + ("tests/t_kdf_hkdf.nim", false), + # Primitives # ---------------------------------------------------------- ("tests/math/t_primitives.nim", false), @@ -188,29 +206,12 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # ---------------------------------------------------------- ("tests/math/t_fr.nim", false), - # Hashing vs OpenSSL - # ---------------------------------------------------------- - ("tests/t_hash_sha256_vs_openssl.nim", true), # skip OpenSSL tests on Windows - # Hashing to elliptic curves # ---------------------------------------------------------- ("tests/t_hash_to_field.nim", false), ("tests/t_hash_to_curve_random.nim", false), ("tests/t_hash_to_curve.nim", false), - # Ciphers - # ---------------------------------------------------------- - ("tests/t_cipher_chacha20.nim", false), - - # Message Authentication Code - # ---------------------------------------------------------- - ("tests/t_mac_poly1305.nim", false), - ("tests/t_mac_hmac_sha256.nim", false), - - # KDF - # ---------------------------------------------------------- - ("tests/t_kdf_hkdf.nim", false), - # Protocols # ---------------------------------------------------------- ("tests/t_ethereum_evm_precompiles.nim", false), @@ -218,6 +219,26 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ ("tests/t_ethereum_eip2333_bls12381_key_derivation.nim", false), ] +const benchDesc = [ + "bench_fp", + "bench_fp_double_precision", + "bench_fp2", + "bench_fp6", + "bench_fp12", + "bench_ec_g1", + "bench_ec_g2", + "bench_pairing_bls12_377", + "bench_pairing_bls12_381", + "bench_pairing_bn254_nogami", + "bench_pairing_bn254_snarks", + "bench_summary_bls12_377", + "bench_summary_bls12_381", + "bench_summary_bn254_nogami", + "bench_summary_bn254_snarks", + "bench_sha256", + "bench_hash_to_curve" +] + # For temporary (hopefully) investigation that can only be reproduced in CI const useDebug = [ "tests/math/t_bigints.nim", @@ -256,13 +277,11 @@ else: # ---------------------------------------------------------------- proc clearParallelBuild() = - exec "> " & buildParallel + # Support clearing from non POSIX shell like CMD, Powershell or MSYS2 + if fileExists(buildParallel): + rmFile(buildParallel) -proc test(flags, path: string, commandFile = false) = - # commandFile should be a "file" but Nimscript doesn't support IO - if not dirExists "build": - mkDir "build" - # Compilation language is controlled by WEAVE_TEST_LANG +template setupCommand(): untyped {.dirty.} = var lang = "c" if existsEnv"TEST_LANG": lang = getEnv"TEST_LANG" @@ -280,36 +299,55 @@ proc test(flags, path: string, commandFile = false) = " --nimcache:nimcache/" & path & " " & path - if not commandFile: - echo "\n==============================================================================================" - echo "Running [flags:", flags, "] ", path - echo "==============================================================================================" - exec command - else: - exec "echo \'" & command & "\' >> " & buildParallel - exec "echo \"------------------------------------------------------\"" +proc test(cmd: string) = + echo "\n==============================================================================================" + echo "Running '", cmd, "'" + echo "==============================================================================================" + exec cmd -proc buildBench(benchName: string, compiler = "", useAsm = true, run = false) = - if not dirExists "build": - mkDir "build" +proc testBatch(commands: var string, flags, path: string) = + setupCommand() + commands &= command & '\n' +template setupBench(): untyped {.dirty.} = let runFlag = if run: " -r " - else: " " + else: " " + + var lang = " c " + if existsEnv"TEST_LANG": + lang = getEnv"TEST_LANG" var cc = "" if compiler != "": cc = "--cc:" & compiler + elif existsEnv"CC": + cc = " --cc:" & getEnv"CC" + if not useAsm: cc &= " -d:CttASM=false" - exec "nim c " & cc & + let command = "nim " & lang & cc & " -d:danger --verbosity:0 -o:build/bench/" & benchName & "_" & compiler & "_" & (if useAsm: "useASM" else: "noASM") & " --nimcache:nimcache/" & benchName & "_" & compiler & "_" & (if useAsm: "useASM" else: "noASM") & runFlag & "--hints:off --warnings:off benchmarks/" & benchName & ".nim" proc runBench(benchName: string, compiler = "", useAsm = true) = - buildBench(benchName, compiler, useAsm, run = true) + if not dirExists "build": + mkDir "build" + let run = true + setupBench() + exec command -proc runTests(requireGMP: bool, dumpCmdFile = false, test32bit = false, testASM = true) = +proc buildBenchBatch(commands: var string, benchName: string, compiler = "", useAsm = true) = + let run = false + let compiler = "" + setupBench() + commands &= command & '\n' + +proc addTestSet(cmdFile: var string, requireGMP: bool, test32bit = false, testASM = true) = + if not dirExists "build": + mkDir "build" + echo "Found " & $testDesc.len & " tests to run." + for td in testDesc: if not(td.useGMP and not requireGMP): var flags = "" @@ -321,29 +359,15 @@ proc runTests(requireGMP: bool, dumpCmdFile = false, test32bit = false, testASM flags &= " -d:debugConstantine" if td.path notin skipSanitizers: flags &= sanitizers - test flags, td.path, dumpCmdFile + + cmdFile.testBatch(flags, td.path) -proc buildAllBenches(useAsm = true) = - echo "\n\n------------------------------------------------------\n" - echo "Building benchmarks to ensure they stay relevant ..." - buildBench("bench_fp", useAsm = useAsm) - buildBench("bench_fp_double_precision", useAsm = useAsm) - buildBench("bench_fp2", useAsm = useAsm) - buildBench("bench_fp6", useAsm = useAsm) - buildBench("bench_fp12", useAsm = useAsm) - buildBench("bench_ec_g1", useAsm = useAsm) - buildBench("bench_ec_g2", useAsm = useAsm) - buildBench("bench_pairing_bls12_377", useAsm = useAsm) - buildBench("bench_pairing_bls12_381", useAsm = useAsm) - buildBench("bench_pairing_bn254_nogami", useAsm = useAsm) - buildBench("bench_pairing_bn254_snarks", useAsm = useAsm) - buildBench("bench_summary_bls12_377", useAsm = useAsm) - buildBench("bench_summary_bls12_381", useAsm = useAsm) - buildBench("bench_summary_bn254_nogami", useAsm = useAsm) - buildBench("bench_summary_bn254_snarks", useAsm = useAsm) - buildBench("bench_sha256", useAsm = useAsm) - buildBench("bench_hash_to_curve", useAsm = useAsm) - echo "All benchmarks compile successfully." +proc addBenchSet(cmdFile: var string, useAsm = true) = + if not dirExists "build": + mkDir "build" + echo "Found " & $benchDesc.len & " benches to compile. (compile-only to ensure they stay relevant)" + for bd in benchDesc: + cmdFile.buildBenchBatch(bd, useASM = useASM) proc genBindings(bindingsName, prefixNimMain: string) = proc compile(libName: string, flags = "") = @@ -379,6 +403,9 @@ proc genHeaders(bindingsName: string) = " --nimcache:nimcache/bindings/" & bindingsName & "_header" & " bindings/" & bindingsName & ".nim" +proc genParallelCmdRunner() = + exec "nim c --verbosity:0 --hints:off --warnings:off -d:release --out:build/pararun --nimcache:nimcache/pararun helpers/pararun.nim" + # Tasks # ---------------------------------------------------------------- @@ -390,123 +417,79 @@ task bindings, "Generate Constantine bindings": task test, "Run all tests": # -d:testingCurves is configured in a *.nim.cfg for convenience - runTests(requireGMP = true) + var cmdFile: string + cmdFile.addTestSet(requireGMP = true, testASM = true) + cmdFile.addBenchSet(useASM = true) # Build (but don't run) benches to ensure they stay relevant + for cmd in cmdFile.splitLines(): + exec cmd - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # runTests(requireGMP = true, test32bit = true) - - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches() - -task test_no_assembler, "Run all tests": +task test_no_asm, "Run all tests (no assembly)": # -d:testingCurves is configured in a *.nim.cfg for convenience - runTests(requireGMP = true, testASM = false) - - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # runTests(requireGMP = true, test32bit = true) - - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches(useASM = false) + var cmdFile: string + cmdFile.addTestSet(requireGMP = true, testASM = false) + cmdFile.addBenchSet(useASM = false) # Build (but don't run) benches to ensure they stay relevant + for cmd in cmdFile.splitLines(): + exec cmd task test_no_gmp, "Run tests that don't require GMP": # -d:testingCurves is configured in a *.nim.cfg for convenience - runTests(requireGMP = false) + var cmdFile: string + cmdFile.addTestSet(requireGMP = false, testASM = true) + cmdFile.addBenchSet(useASM = true) # Build (but don't run) benches to ensure they stay relevant + for cmd in cmdFile.splitLines(): + exec cmd - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # runTests(requireGMP = true, test32bit = true) - - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches() - -task test_no_gmp_no_assembler, "Run tests that don't require GMP using a pure Nim backend": +task test_no_gmp_no_asm, "Run tests that don't require GMP using a pure Nim backend": # -d:testingCurves is configured in a *.nim.cfg for convenience - runTests(requireGMP = false, testASM = false) - - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # runTests(requireGMP = true, test32bit = true) - - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches() + var cmdFile: string + cmdFile.addTestSet(requireGMP = false, testASM = false) + cmdFile.addBenchSet(useASM = false) # Build (but don't run) benches to ensure they stay relevant + for cmd in cmdFile.splitLines(): + exec cmd task test_parallel, "Run all tests in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience clearParallelBuild() - runTests(requireGMP = true, dumpCmdFile = true) - exec "parallel --keep-order --group < " & buildParallel + genParallelCmdRunner() - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # clearParallelBuild() - # runTests(requireGMP = true, dumpCmdFile = true, test32bit = true) - # exec "parallel --keep-order --group < " & buildParallel + var cmdFile: string + cmdFile.addTestSet(requireGMP = true, testASM = true) + cmdFile.addBenchSet(useASM = true) # Build (but don't run) benches to ensure they stay relevant + writeFile(buildParallel, cmdFile) + exec "build/pararun " & buildParallel - # Now run the benchmarks - # - # Benchmarks compile - # ignore Windows 32-bit for the moment - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches() - -task test_parallel_no_assembler, "Run all tests (without macro assembler) in parallel (via GNU parallel)": +task test_parallel_no_asm, "Run all tests (without macro assembler) in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience clearParallelBuild() - runTests(requireGMP = true, dumpCmdFile = true, testASM = false) - exec "parallel --keep-order --group < " & buildParallel + genParallelCmdRunner() - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # clearParallelBuild() - # runTests(requireGMP = true, dumpCmdFile = true, test32bit = true, testASM = false) - # exec "parallel --keep-order --group < " & buildParallel - - # Now run the benchmarks - # - # Benchmarks compile - # ignore Windows 32-bit for the moment - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches(useASM = false) + var cmdFile: string + cmdFile.addTestSet(requireGMP = true, testASM = false) + cmdFile.addBenchSet(useASM = false) + writeFile(buildParallel, cmdFile) + exec "build/pararun " & buildParallel task test_parallel_no_gmp, "Run all tests in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience clearParallelBuild() - runTests(requireGMP = false, dumpCmdFile = true) - exec "parallel --keep-order --group < " & buildParallel + genParallelCmdRunner() - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # clearParallelBuild() - # runTests(requireGMP = false, dumpCmdFile = true, test32bit = true) - # exec "parallel --keep-order --group < " & buildParallel + var cmdFile: string + cmdFile.addTestSet(requireGMP = false, testASM = true) + cmdFile.addBenchSet(useASM = true) # Build (but don't run) benches to ensure they stay relevant + writeFile(buildParallel, cmdFile) + exec "build/pararun " & buildParallel - # Now run the benchmarks - # - # Benchmarks compile - # ignore Windows 32-bit for the moment - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches() - -task test_parallel_no_gmp_no_assembler, "Run all tests in parallel (via GNU parallel)": +task test_parallel_no_gmp_no_asm, "Run all tests in parallel (via GNU parallel)": # -d:testingCurves is configured in a *.nim.cfg for convenience clearParallelBuild() - runTests(requireGMP = false, dumpCmdFile = true, testASM = false) - exec "parallel --keep-order --group < " & buildParallel + genParallelCmdRunner() - # if sizeof(int) == 8: # 32-bit tests on 64-bit arch - # clearParallelBuild() - # runTests(requireGMP = false, dumpCmdFile = true, test32bit = true, testASM = false) - # exec "parallel --keep-order --group < " & buildParallel - - # Now run the benchmarks - # - # Benchmarks compile - # ignore Windows 32-bit for the moment - # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment - if not defined(windows) or not (existsEnv"UCPU" or getEnv"UCPU" == "i686"): - buildAllBenches(useASM = false) + var cmdFile: string + cmdFile.addTestSet(requireGMP = false, testASM = false) + cmdFile.addBenchSet(useASM = false) # Build (but don't run) benches to ensure they stay relevant + writeFile(buildParallel, cmdFile) + exec "build/pararun " & buildParallel # Finite field 𝔽p # ------------------------------------------ diff --git a/helpers/pararun.nim b/helpers/pararun.nim new file mode 100644 index 0000000..8eeff4b --- /dev/null +++ b/helpers/pararun.nim @@ -0,0 +1,151 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + std/[os, strutils, cpuinfo, strformat, deques, terminal], + std/[asyncfutures, asyncdispatch], + asynctools/[asyncproc, asyncpipe, asyncsync] + +# Pararun is a parallel shell command runner +# ------------------------------------------ +# Usage: pararun 0: + let waiter = s.waiters.popFirst() + waiter.complete() + + doAssert s.slots in {0..s.max} + +# Task runner +# ---------------------------------------------------------------- + +type WorkQueue = ref object + sem: AsyncSemaphore + cmdQueue: Deque[string] + outputQueue: AsyncQueue[tuple[cmd: string, p: AsyncProcess]] + lineBuf: string + +proc releaseOnProcessExit(sem: AsyncSemaphore, p: AsyncProcess) {.async.} = + # TODO: addProcess callback on exit is cleaner but locks the AsyncPipe "readInto" + # + # p.processID.addProcess do (fd: AsyncFD) -> bool: + # sem.release() + # + # see also: https://forum.nim-lang.org/t/5565 + + while p.running(): + await sleepAsync(10) + sem.release() + +proc enqueuePendingCommands(wq: WorkQueue) {.async.} = + while wq.cmdQueue.len > 0: + await wq.sem.acquire() + let cmd = wq.cmdQueue.popFirst() + let p = cmd.startProcess( + options = {poStdErrToStdOut, poUsePath, poEvalCommand} + ) + + asyncCheck wq.sem.releaseOnProcessExit(p) + wq.outputQueue.putNoWait((cmd, p)) + +proc flushCommandsOutput(wq: WorkQueue) {.async.} = + while true: + let (cmd, p) = await wq.outputQueue.get() + + echo '\n', '='.repeat(80) + echo "||\n|| Running: ", cmd ,"\n||" + echo '='.repeat(80) + + while true: + let charsRead = await p.outputHandle.readInto(wq.lineBuf[0].addr, wq.lineBuf.len) + if charsRead == 0: + break + let charsWritten = stdout.writeBuffer(wq.lineBuf[0].addr, charsRead) + doAssert charsRead == charsWritten + + if wq.cmdQueue.len == 0 and wq.outputQueue.len == 0: + return + +proc runCommands(commandFile: string, numWorkers: int) = + # State + # ----- + + let wq = WorkQueue( + sem: AsyncSemaphore.new(numWorkers), + cmdQueue: initDeque[string](), + outputQueue: newAsyncQueue[tuple[cmd: string, p: AsyncProcess]](), + lineBuf: newString(max(80, terminalWidth())) + ) + + # Parse the file + # -------------- + for cmd in lines(commandFile): + if cmd.len == 0: continue + wq.cmdQueue.addLast(cmd) + + echo "Found ", wq.cmdQueue.len, " commands to run" + + # Run the commands + # ---------------- + asyncCheck wq.enqueuePendingCommands() + waitFor wq.flushCommandsOutput() + +# Main +# ---------------------------------------------------------------- + +proc main() = + var commandFile: string + var numWorkers = countProcessors() + + if paramCount() == 0: + let exeName = getAppFilename().extractFilename() + echo &"Usage: {exeName} " + + if paramCount() >= 1: + commandFile = paramStr(1) + + if paramCount() == 2: + numWorkers = paramStr(2).parseInt() + + if paramCount() > 2: + let exeName = getAppFilename().extractFilename() + echo &"Usage: {exeName} " + quit 1 + + runCommands(commandFile, numWorkers) + +when isMainModule: + main() \ No newline at end of file diff --git a/helpers/prng_unsafe.nim b/helpers/prng_unsafe.nim index be95c33..211b629 100644 --- a/helpers/prng_unsafe.nim +++ b/helpers/prng_unsafe.nim @@ -158,9 +158,19 @@ func random_unsafe(rng: var RngState, a: var Limbs) = for i in 0 ..< a.len: a[i] = SecretWord(rng.next()) +template clearExtraBitsOverMSB(a: var BigInt) = + ## If we do bit manipulation at the word level, + ## for example a 381-bit BigInt stored in a 384-bit buffer + ## we need to clear the upper 3-bit + when a.bits != a.limbs.len * WordBitWidth: + const posExtraBits = a.bits - (a.limbs.len-1) * WordBitWidth + const mask = (One shl posExtraBits) - One + a.limbs[^1] = a.limbs[^1] and mask + func random_unsafe(rng: var RngState, a: var BigInt) = ## Initialize a standalone BigInt rng.random_unsafe(a.limbs) + a.clearExtraBitsOverMSB() func random_unsafe(rng: var RngState, a: var FF) = ## Initialize a Field element @@ -193,6 +203,7 @@ func random_highHammingWeight(rng: var RngState, a: var BigInt) = ## with high Hamming weight ## to have a higher probability of triggering carries rng.random_highHammingWeight(a.limbs) + a.clearExtraBitsOverMSB() func random_highHammingWeight(rng: var RngState, a: var FF) = ## Recursively initialize a BigInt (part of a field) or Field element @@ -236,6 +247,7 @@ func random_long01Seq(rng: var RngState, a: var BigInt) = a.unmarshal(buf, bigEndian) else: a.unmarshal(buf, littleEndian) + a.clearExtraBitsOverMSB() func random_long01Seq(rng: var RngState, a: var Limbs) = ## Initialize standalone limbs diff --git a/tests/math/t_bigints_mod_vs_gmp.nim b/tests/math/t_bigints_mod_vs_gmp.nim index c4c1ade..d46ffe9 100644 --- a/tests/math/t_bigints_mod_vs_gmp.nim +++ b/tests/math/t_bigints_mod_vs_gmp.nim @@ -14,7 +14,9 @@ import # Internal ../../constantine/math/io/io_bigints, ../../constantine/math/arithmetic, - ../../constantine/platforms/primitives + ../../constantine/platforms/primitives, + # Test utilities + ../../helpers/prng_unsafe echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow @@ -82,15 +84,11 @@ const # https://gmplib.org/manual/Integer-Import-and-Export.html GMP_LeastSignificantWordFirst = -1'i32 proc main() = - var gmpRng: gmp_randstate_t - gmp_randinit_mt(gmpRng) - # The GMP seed varies between run so that - # test coverage increases as the library gets tested. - # This requires to dump the seed in the console or the function inputs - # to be able to reproduce a bug + var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 - echo "GMP seed: ", seed - gmp_randseed_ui(gmpRng, seed) + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo "test_bigints_mod_vs_gmp xoshiro512** seed: ", seed var a, m, r: mpz_t mpz_init(a) @@ -101,34 +99,25 @@ proc main() = # echo "--------------------------------------------------------------------------------" echo "Testing: random dividend (" & align($aBits, 4) & "-bit) -- random modulus (" & align($mBits, 4) & "-bit)" - # Generate random value in the range 0 ..< 2^aBits - mpz_urandomb(a, gmpRng, aBits) - # Generate random modulus and ensure the MSB is set - mpz_urandomb(m, gmpRng, mBits) - mpz_setbit(m, mBits-1) - - # discard gmp_printf(" -- %#Zx mod %#Zx\n", a.addr, m.addr) + # Build the bigints + let aTest = rng.random_unsafe(BigInt[aBits]) + var mTest = rng.random_unsafe(BigInt[mBits]) + # Ensure modulus MSB is set + mTest.setBit(mBits-1) ######################################################### - # Conversion buffers + # Conversion to GMP const aLen = (aBits + 7) div 8 const mLen = (mBits + 7) div 8 var aBuf: array[aLen, byte] var mBuf: array[mLen, byte] - var aW, mW: csize # Word written by GMP + aBuf.marshal(aTest, bigEndian) + mBuf.marshal(mTest, bigEndian) - discard mpz_export(aBuf[0].addr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a) - discard mpz_export(mBuf[0].addr, mW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, m) - - # Since the modulus is using all bits, it's we can test for exact amount copy - doAssert aLen >= aW, "Expected at most " & $aLen & " bytes but wrote " & $aW & " for " & toHex(aBuf) & " (big-endian)" - doAssert mLen == mW, "Expected " & $mLen & " bytes but wrote " & $mW & " for " & toHex(mBuf) & " (big-endian)" - - # Build the bigint - let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian) - let mTest = BigInt[mBits].unmarshal(mBuf.toOpenArray(0, mW-1), bigEndian) + mpz_import(a, aLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, aBuf[0].addr) + mpz_import(m, mLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, mBuf[0].addr) ######################################################### # Modulus @@ -139,8 +128,12 @@ proc main() = ######################################################### # Check + + {.push warnings: off.} # deprecated csize + var aW, mW, rW: csize # Words written by GMP + {.pop.} + var rGMP: array[mLen, byte] - var rW: csize # Word written by GMP discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r) var rConstantine: array[mLen, byte] diff --git a/tests/math/t_bigints_mul_high_words_vs_gmp.nim b/tests/math/t_bigints_mul_high_words_vs_gmp.nim index 6159e40..27fe969 100644 --- a/tests/math/t_bigints_mul_high_words_vs_gmp.nim +++ b/tests/math/t_bigints_mul_high_words_vs_gmp.nim @@ -14,7 +14,9 @@ import # Internal ../../constantine/math/io/io_bigints, ../../constantine/math/arithmetic, - ../../constantine/platforms/abstractions + ../../constantine/platforms/abstractions, + # Test utilities + ../../helpers/prng_unsafe echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow @@ -51,15 +53,11 @@ const # https://gmplib.org/manual/Integer-Import-and-Export.html GMP_LeastSignificantWordFirst {.used.} = -1'i32 proc main() = - var gmpRng: gmp_randstate_t - gmp_randinit_mt(gmpRng) - # The GMP seed varies between run so that - # test coverage increases as the library gets tested. - # This requires to dump the seed in the console or the function inputs - # to be able to reproduce a bug + var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 - echo "GMP seed: ", seed - gmp_randseed_ui(gmpRng, seed) + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo "test_bigints_mod_vs_gmp xoshiro512** seed: ", seed var r, a, b: mpz_t mpz_init(r) @@ -74,36 +72,23 @@ proc main() = "-bit) * b (", align($bBits, 4), "-bit) (full mul bits: ", align($(aBits+bBits), 4), "), r large enough? ", wordsRequired(rBits) >= wordsRequired(aBits+bBits) - wordsStartIndex - # Generate random value in the range 0 ..< 2^aBits - mpz_urandomb(a, gmpRng, aBits) - # Generate random modulus and ensure the MSB is set - mpz_urandomb(b, gmpRng, bBits) - mpz_setbit(r, aBits+bBits) - - # discard gmp_printf(" -- %#Zx mod %#Zx\n", a.addr, m.addr) + # Build the bigints + let aTest = rng.random_unsafe(BigInt[aBits]) + var bTest = rng.random_unsafe(BigInt[bBits]) ######################################################### - # Conversion buffers + # Conversion to GMP const aLen = (aBits + 7) div 8 const bLen = (bBits + 7) div 8 var aBuf: array[aLen, byte] var bBuf: array[bLen, byte] - {.push warnings: off.} # deprecated csize - var aW, bW: csize # Word written by GMP - {.pop.} + aBuf.marshal(aTest, bigEndian) + bBuf.marshal(bTest, bigEndian) - discard mpz_export(aBuf[0].addr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a) - discard mpz_export(bBuf[0].addr, bW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, b) - - # Since the modulus is using all bits, it's we can test for exact amount copy - doAssert aLen >= aW, "Expected at most " & $aLen & " bytes but wrote " & $aW & " for " & toHex(aBuf) & " (big-endian)" - doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)" - - # Build the bigint - let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian) - let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian) + mpz_import(a, aLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, aBuf[0].addr) + mpz_import(b, bLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, bBuf[0].addr) ######################################################### # Multiplication + drop low words @@ -124,11 +109,13 @@ proc main() = ######################################################### # Check + + {.push warnings: off.} # deprecated csize + var aW, bW, rW: csize # Word written by GMP + {.pop.} + const rLen = numWords * WordBitWidth var rGMP: array[rLen, byte] - {.push warnings: off.} # deprecated csize - var rW: csize # Word written by GMP - {.pop.} discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r) var rConstantine: array[rLen, byte] diff --git a/tests/math/t_bigints_mul_vs_gmp.nim b/tests/math/t_bigints_mul_vs_gmp.nim index 2e0a10d..45eee4b 100644 --- a/tests/math/t_bigints_mul_vs_gmp.nim +++ b/tests/math/t_bigints_mul_vs_gmp.nim @@ -14,7 +14,9 @@ import # Internal ../../constantine/math/io/io_bigints, ../../constantine/math/arithmetic, - ../../constantine/platforms/abstractions + ../../constantine/platforms/abstractions, + # Test utilities + ../../helpers/prng_unsafe echo "\n------------------------------------------------------\n" # We test up to 1024-bit, more is really slow @@ -48,15 +50,11 @@ const # https://gmplib.org/manual/Integer-Import-and-Export.html GMP_LeastSignificantWordFirst {.used.} = -1'i32 proc main() = - var gmpRng: gmp_randstate_t - gmp_randinit_mt(gmpRng) - # The GMP seed varies between run so that - # test coverage increases as the library gets tested. - # This requires to dump the seed in the console or the function inputs - # to be able to reproduce a bug + var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 - echo "GMP seed: ", seed - gmp_randseed_ui(gmpRng, seed) + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo "test_bigints_mod_vs_gmp xoshiro512** seed: ", seed var r, a, b: mpz_t mpz_init(r) @@ -67,36 +65,23 @@ proc main() = # echo "--------------------------------------------------------------------------------" echo "Testing: random mul r (", align($rBits, 4), "-bit) <- a (", align($aBits, 4), "-bit) * b (", align($bBits, 4), "-bit) (full mul bits: ", align($(aBits+bBits), 4), "), r large enough? ", rBits >= aBits+bBits - # Generate random value in the range 0 ..< 2^aBits - mpz_urandomb(a, gmpRng, aBits) - # Generate random modulus and ensure the MSB is set - mpz_urandomb(b, gmpRng, bBits) - mpz_setbit(r, aBits+bBits) - - # discard gmp_printf(" -- %#Zx mod %#Zx\n", a.addr, m.addr) + # Build the bigints + let aTest = rng.random_unsafe(BigInt[aBits]) + var bTest = rng.random_unsafe(BigInt[bBits]) ######################################################### - # Conversion buffers + # Conversion to GMP const aLen = (aBits + 7) div 8 const bLen = (bBits + 7) div 8 var aBuf: array[aLen, byte] var bBuf: array[bLen, byte] - {.push warnings: off.} # deprecated csize - var aW, bW: csize # Word written by GMP - {.pop.} + aBuf.marshal(aTest, bigEndian) + bBuf.marshal(bTest, bigEndian) - discard mpz_export(aBuf[0].addr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a) - discard mpz_export(bBuf[0].addr, bW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, b) - - # Since the modulus is using all bits, it's we can test for exact amount copy - doAssert aLen >= aW, "Expected at most " & $aLen & " bytes but wrote " & $aW & " for " & toHex(aBuf) & " (big-endian)" - doAssert bLen >= bW, "Expected at most " & $bLen & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)" - - # Build the bigint - let aTest = BigInt[aBits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian) - let bTest = BigInt[bBits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian) + mpz_import(a, aLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, aBuf[0].addr) + mpz_import(b, bLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, bBuf[0].addr) ######################################################### # Multiplication @@ -114,11 +99,13 @@ proc main() = ######################################################### # Check + + {.push warnings: off.} # deprecated csize + var aW, bW, rW: csize # Word written by GMP + {.pop.} + const rLen = numWords * WordBitWidth var rGMP: array[rLen, byte] - {.push warnings: off.} # deprecated csize - var rW: csize # Word written by GMP - {.pop.} discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r) var rConstantine: array[rLen, byte] diff --git a/tests/math/t_finite_fields_vs_gmp.nim b/tests/math/t_finite_fields_vs_gmp.nim index 89335a7..a8b4484 100644 --- a/tests/math/t_finite_fields_vs_gmp.nim +++ b/tests/math/t_finite_fields_vs_gmp.nim @@ -15,7 +15,9 @@ import ../../constantine/platforms/abstractions, ../../constantine/math/io/[io_bigints, io_fields], ../../constantine/math/arithmetic, - ../../constantine/math/config/curves + ../../constantine/math/config/curves, + # Test utilities + ../../helpers/prng_unsafe echo "\n------------------------------------------------------\n" @@ -45,36 +47,32 @@ const # https://gmplib.org/manual/Integer-Import-and-Export.html # Factor common things in proc to avoid generating 100k+ lines of C code proc binary_prologue[C: static Curve, N: static int]( - gmpRng: var gmp_randstate_t, + rng: var RngState, a, b, p: var mpz_t, aTest, bTest: var Fp[C], aBuf, bBuf: var array[N, byte]) = - const bits = C.getCurveBitwidth() - # Generate random value in the range 0 ..< 2^(bits-1) - mpz_urandomb(a, gmpRng, uint bits) - mpz_urandomb(b, gmpRng, uint bits) + # Build the field elements + aTest = rng.random_unsafe(Fp[C]) + bTest = rng.random_unsafe(Fp[C]) + # Set modulus to curve modulus let err = mpz_set_str(p, Curve(C).Mod.toHex(), 0) doAssert err == 0, "Error on prime for curve " & $Curve(C) ######################################################### - # Conversion buffers - const len = (bits + 7) div 8 + # Conversion to GMP + const aLen = (C.getCurveBitwidth() + 7) div 8 + const bLen = (C.getCurveBitwidth() + 7) div 8 - static: doAssert N >= len + var aBuf: array[aLen, byte] + var bBuf: array[bLen, byte] - var aW, bW: csize # Word written by GMP - discard mpz_export(aBuf[0].addr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a) - discard mpz_export(bBuf[0].addr, bW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, b) + aBuf.marshal(aTest, bigEndian) + bBuf.marshal(bTest, bigEndian) - # Since the modulus is using all bits, it's we can test for exact amount copy - doAssert len >= aW, "Expected at most " & $len & " bytes but wrote " & $aW & " for " & toHex(aBuf) & " (big-endian)" - doAssert len >= bW, "Expected at most " & $len & " bytes but wrote " & $bW & " for " & toHex(bBuf) & " (big-endian)" - - # Build the bigint - aTest = Fp[C].fromBig BigInt[bits].unmarshal(aBuf.toOpenArray(0, aW-1), bigEndian) - bTest = Fp[C].fromBig BigInt[bits].unmarshal(bBuf.toOpenArray(0, bW-1), bigEndian) + mpz_import(a, aLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, aBuf[0].addr) + mpz_import(b, bLen, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, bBuf[0].addr) proc binary_epilogue[C: static Curve, N: static int]( r, a, b: mpz_t, @@ -85,8 +83,12 @@ proc binary_epilogue[C: static Curve, N: static int]( ######################################################### # Check + + {.push warnings: off.} # deprecated csize + var aW, bW, rW: csize # Word written by GMP + {.pop.} + var rGMP: array[N, byte] - var rW: csize # Word written by GMP discard mpz_export(rGMP[0].addr, rW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, r) var rConstantine: array[N, byte] @@ -95,7 +97,6 @@ proc binary_epilogue[C: static Curve, N: static int]( # Note: in bigEndian, GMP aligns left while constantine aligns right doAssert rGMP.toOpenArray(0, rW-1) == rConstantine.toOpenArray(N-rW, N-1), block: # Reexport as bigEndian for debugging - var aW, bW: csize discard mpz_export(aBuf[0].unsafeAddr, aW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, a) discard mpz_export(bBuf[0].unsafeAddr, bW.addr, GMP_MostSignificantWordFirst, 1, GMP_WordNativeEndian, 0, b) "\nModular " & operation & " on curve " & $C & " with operands\n" & @@ -112,7 +113,7 @@ proc binary_epilogue[C: static Curve, N: static int]( # # ############################################################ -proc addTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curve) = +proc addTests(rng: var RngState, a, b, p, r: var mpz_t, C: static Curve) = # echo "Testing: random modular addition on ", $C const @@ -121,7 +122,7 @@ proc addTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv var aTest, bTest{.noInit.}: Fp[C] aBuf, bBuf: array[bufLen, byte] - binary_prologue(gmpRng, a, b, p, aTest, bTest, aBuf, bBuf) + binary_prologue(rng, a, b, p, aTest, bTest, aBuf, bBuf) mpz_add(r, a, b) mpz_mod(r, r, p) @@ -135,7 +136,7 @@ proc addTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv binary_epilogue(r, a, b, rTest, aBuf, bBuf, "Addition (with result)") binary_epilogue(r, a, b, r2Test, aBuf, bBuf, "Addition (in-place)") -proc subTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curve) = +proc subTests(rng: var RngState, a, b, p, r: var mpz_t, C: static Curve) = # echo "Testing: random modular substraction on ", $C const @@ -144,7 +145,7 @@ proc subTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv var aTest, bTest{.noInit.}: Fp[C] aBuf, bBuf: array[bufLen, byte] - binary_prologue(gmpRng, a, b, p, aTest, bTest, aBuf, bBuf) + binary_prologue(rng, a, b, p, aTest, bTest, aBuf, bBuf) mpz_sub(r, a, b) mpz_mod(r, r, p) @@ -163,7 +164,7 @@ proc subTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv binary_epilogue(r, a, b, r2Test, aBuf, bBuf, "Substraction (in-place)") binary_epilogue(r, a, b, r3Test, aBuf, bBuf, "Substraction (result aliasing)") -proc mulTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curve) = +proc mulTests(rng: var RngState, a, b, p, r: var mpz_t, C: static Curve) = # echo "Testing: random modular multiplication on ", $C const @@ -172,7 +173,7 @@ proc mulTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv var aTest, bTest{.noInit.}: Fp[C] aBuf, bBuf: array[bufLen, byte] - binary_prologue(gmpRng, a, b, p, aTest, bTest, aBuf, bBuf) + binary_prologue(rng, a, b, p, aTest, bTest, aBuf, bBuf) mpz_mul(r, a, b) mpz_mod(r, r, p) @@ -186,7 +187,7 @@ proc mulTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv binary_epilogue(r, a, b, rTest, aBuf, bBuf, "Multiplication (with result)") binary_epilogue(r, a, b, r2Test, aBuf, bBuf, "Multiplication (in-place)") -proc invTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curve) = +proc invTests(rng: var RngState, a, b, p, r: var mpz_t, C: static Curve) = # We use the binary prologue epilogue but the "b" parameter is actual unused # echo "Testing: random modular inversion on ", $C @@ -196,7 +197,7 @@ proc invTests(gmpRng: var gmp_randstate_t, a, b, p, r: var mpz_t, C: static Curv var aTest, bTest{.noInit.}: Fp[C] aBuf, bBuf: array[bufLen, byte] - binary_prologue(gmpRng, a, b, p, aTest, bTest, aBuf, bBuf) + binary_prologue(rng, a, b, p, aTest, bTest, aBuf, bBuf) let exist = mpz_invert(r, a, p) doAssert exist != 0 @@ -227,15 +228,11 @@ macro randomTests(numTests: static int, curveSym, body: untyped): untyped = `body` template testSetup {.dirty.} = - var gmpRng: gmp_randstate_t - gmp_randinit_mt(gmpRng) - # The GMP seed varies between run so that - # test coverage increases as the library gets tested. - # This requires to dump the seed in the console or the function inputs - # to be able to reproduce a bug + var rng: RngState let seed = uint32(getTime().toUnix() and (1'i64 shl 32 - 1)) # unixTime mod 2^32 - echo "GMP seed: ", seed - gmp_randseed_ui(gmpRng, seed) + rng.seed(seed) + echo "\n------------------------------------------------------\n" + echo "test_finite_fields_vs_gmp** seed: ", seed var a, b, p, r: mpz_t mpz_init(a) @@ -247,25 +244,25 @@ proc mainMul() = testSetup() echo "Testing modular multiplications vs GMP" randomTests(24, curve): - mulTests(gmpRng, a, b, p, r, curve) + mulTests(rng, a, b, p, r, curve) proc mainAdd() = testSetup() echo "Testing modular additions vs GMP" randomTests(24, curve): - addTests(gmpRng, a, b, p, r, curve) + addTests(rng, a, b, p, r, curve) proc mainSub() = testSetup() echo "Testing modular substractions vs GMP" randomTests(24, curve): - subTests(gmpRng, a, b, p, r, curve) + subTests(rng, a, b, p, r, curve) proc mainInv() = testSetup() echo "Testing modular inversions vs GMP" randomTests(24, curve): - invTests(gmpRng, a, b, p, r, curve) + invTests(rng, a, b, p, r, curve) mainMul() diff --git a/tests/t_hash_sha256_vs_openssl.nim b/tests/t_hash_sha256_vs_openssl.nim index def82c3..be08b90 100644 --- a/tests/t_hash_sha256_vs_openssl.nim +++ b/tests/t_hash_sha256_vs_openssl.nim @@ -43,8 +43,8 @@ proc SHA256_OpenSSL[T: byte|char]( # -------------------------------------------------------------------- echo "\n------------------------------------------------------\n" -const SmallSizeIters = 128 -const LargeSizeIters = 10 +const SmallSizeIters = 64 +const LargeSizeIters = 1 proc sanityABC = var bufCt: array[32, byte]