Mamy Ratsimbazafy 9a7137466e
C API for Ethereum BLS signatures (#228)
* [testsuite] Rework parallel test runner to buffer beyond 65536 chars and properly wait for process exit

* [testsuite] improve error reporting

* rework openArray[byte/char] for BLS signature C API

* Prepare for optimized library and bindings

* properly link to constantine

* Compiler fixes, global sanitizers, GCC bug with --opt:size

* workaround/fix #229: don't inline field reduction in Fp2

* fix clang running out of registers with LTO

* [C API] missed length parameters for ctt_eth_bls_fast_aggregate_verify

* double-precision asm is too large for inlining, try to fix Linux and MacOS woes at https://github.com/mratsim/constantine/pull/228#issuecomment-1512773460

* Use FORTIFY_SOURCE for testing

* Fix #230 - gcc miscompiles Fp6 mul with LTO

* disable LTO for now, PR is too long
2023-04-18 22:02:23 +02:00

210 lines
5.9 KiB
Nim

# 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/[strformat, strutils, macros, strtabs]
# Overview
# ------------------------------------------------------------
#
# This files provides template for C header generation
proc genHeaderLicense*(): string =
"""
/** 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.
*/
"""
proc genHeader*(name, body: string): string =
&"""
#ifndef __CTT_H_{name}__
#define __CTT_H_{name}__
{body}
#endif
"""
proc genCpp*(body: string): string {.raises:[ValueError].} =
&"""
#ifdef __cplusplus
extern "C" {{
#endif
{body}
#ifdef __cplusplus
}}
#endif
"""
# Types' declarations
# -------------------------------------------
proc genBuiltinsTypes*(): string =
"""
#if defined(__SIZE_TYPE__) && defined(__PTRDIFF_TYPE__)
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
#else
#include <stddef.h>
#endif
#if defined(__UINT8_TYPE__) && defined(__UINT32_TYPE__) && defined(__UINT64_TYPE__)
typedef __UINT8_TYPE__ uint8_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_t;
#else
#include <stdint.h>
#endif
"""
proc genCttBaseTypedef*(): string =
"""
typedef size_t secret_word;
typedef size_t secret_bool;
typedef uint8_t byte;
"""
proc genWordsRequired*(): string =
"""
#define WordBitWidth (sizeof(secret_word)*8)
#define words_required(bits) ((bits+WordBitWidth-1)/WordBitWidth)
"""
proc genField*(name: string, bits: int): string =
&"typedef struct {{ secret_word limbs[words_required({bits})]; }} {name};"
proc genExtField*(name: string, degree: int, basename: string): string =
&"typedef struct {{ {basename} c[{degree}]; }} {name};"
proc genEllipticCurvePoint*(name, coords, basename: string): string =
&"typedef struct {{ {basename} {coords}; }} {name};"
# Nim internals
# -------------------------------------------
proc declNimMain*(libName: string): string =
## Create the NimMain function.
## It initializes:
## - the Nim runtime if seqs, strings or heap-allocated types are used,
## this is the case only if Constantine is multithreaded.
## - runtime CPU features detection
##
## Assumes library is compiled with --nimMainPrefix:ctt_{libName}_
&"""
/*
* Initializes the library:
* - detect CPU features like ADX instructions support (MULX, ADCX, ADOX)
*/
void ctt_{libName}_init_NimMain(void);"""
# Subroutines' declarations
# -------------------------------------------
let TypeMap {.compileTime.} = newStringTable({
"SecretBool": "secret_bool",
"SecretWord": "secret_word"
})
proc toCrettype(node: NimNode): string =
node.expectKind({nnkEmpty, nnkSym})
if node.kind == nnkEmpty:
# align iwth secret_bool and secret_word
"void "
else:
TypeMap[$node]
proc toCtrivialParam(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkSym})
let isVar = typ.kind == nnkVarTy
let constify = if isVar: ""
else: "const "
let sTyp = if isVar: $typ[0]
else: $typ
if sTyp in TypeMap:
# Pass-by-value
constify & TypeMap[sTyp] & " " & name
else:
# Pass-by-reference
constify & sTyp & "* " & name
proc toCparam(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkCall, nnkSym})
if typ.kind == nnkCall:
typ[0].expectKind(nnkOpenSymChoice)
doAssert typ[0][0].eqIdent"[]"
doAssert typ[1].eqIdent"openArray"
let sTyp = $typ[2]
if sTyp in TypeMap:
"const " & TypeMap[sTyp] & " " & name & "[], ptrdiff_t " & name & "_len"
else:
"const " & sTyp & " " & name & "[], ptrdiff_t " & name & "_len"
elif typ.kind == nnkVarTy and typ[0].kind == nnkCall:
typ[0][0].expectKind(nnkOpenSymChoice)
doAssert typ[0][0][0].eqIdent"[]"
doAssert typ[0][1].eqIdent"openArray"
let sTyp = $typ[0][2]
if sTyp in TypeMap:
TypeMap[sTyp] & " " & name & "[], ptrdiff_t " & name & "_len"
else:
sTyp & " " & name & "[], ptrdiff_t " & name & "_len"
else:
toCtrivialParam(name, typ)
macro collectBindings*(cBindingsStr: untyped, body: typed): untyped =
## Collect function definitions from a generator template
body.expectKind(nnkStmtList)
var cBindings: string
for generator in body:
generator.expectKind(nnkStmtList)
for fnDef in generator:
if fnDef.kind notin {nnkProcDef, nnkFuncDef}:
continue
cBindings &= "\n"
# rettype name(pType0* pName0, pType1* pName1, ...);
cBindings &= fnDef.params[0].toCrettype()
cBindings &= ' '
cBindings &= $fnDef.name
cBindings &= '('
for i in 1 ..< fnDef.params.len:
if i != 1: cBindings &= ", "
let paramDef = fnDef.params[i]
paramDef.expectKind(nnkIdentDefs)
let pType = paramDef[^2]
# No default value
paramDef[^1].expectKind(nnkEmpty)
for j in 0 ..< paramDef.len - 2:
if j != 0: cBindings &= ", "
var name = $paramDef[j]
cBindings &= toCparam(name.split('`')[0], pType)
cBindings &= ");"
if defined(CttGenerateHeaders):
result = newConstStmt(cBindingsStr, newLit cBindings)
else:
result = body