Skip to content

Commit

Permalink
Rust bindings (#287)
Browse files Browse the repository at this point in the history
* first jab at Rust bindings

* stash C library and header generation

* Create a single big library with multiple headers

* remove ctt_pure, people will not call crypto proc twice with unchanged parameter and extra noise when reading header

* fix MacOS and Windows builds

* fix cross-lang ThinLTO, require LLD

* Remove NimMain need, cleanup CPU features and detect them on library load
  • Loading branch information
mratsim authored Oct 24, 2023
1 parent c3b76cd commit 3e27f1e
Show file tree
Hide file tree
Showing 57 changed files with 5,950 additions and 2,283 deletions.
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build]
# https://doc.rust-lang.org/rustc/linker-plugin-lto.html
rustflags="-Clinker-plugin-lto -Clinker=clang -Clink-arg=-fuse-ld=lld"
20 changes: 12 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -236,16 +236,18 @@ jobs:
shell: bash
run: |
cd constantine
nimble bindings --verbose
nimble test_bindings --verbose
nimble make_lib --verbose
nimble make_headers --verbose
nimble test_lib --verbose
nimble test_parallel --verbose
- name: Run Constantine tests (UNIX no Assembly)
if: runner.os != 'Windows' && matrix.target.BACKEND == 'NO_ASM'
shell: bash
run: |
cd constantine
CTT_ASM=0 nimble bindings --verbose
nimble test_bindings --verbose
CTT_ASM=0 nimble make_lib --verbose
nimble make_headers --verbose
nimble test_lib --verbose
CTT_ASM=0 nimble test_parallel --verbose
- name: Run Constantine tests (Windows with Assembly)
# So "test_bindings" uses C and can find GMP
Expand All @@ -254,8 +256,9 @@ jobs:
shell: msys2 {0}
run: |
cd constantine
nimble bindings --verbose
nimble test_bindings --verbose
nimble make_lib --verbose
nimble make_headers --verbose
nimble test_lib --verbose
nimble test_parallel_no_gmp --verbose
- name: Run Constantine tests (Windows no Assembly)
# So "test_bindings" uses C and can find GMP
Expand All @@ -264,6 +267,7 @@ jobs:
shell: msys2 {0}
run: |
cd constantine
CTT_ASM=0 nimble bindings --verbose
nimble test_bindings --verbose
CTT_ASM=0 nimble make_lib --verbose
nimble make_headers --verbose
nimble test_lib --verbose
CTT_ASM=0 nimble test_parallel_no_gmp --verbose
40 changes: 35 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,47 @@
nimcache/

# Executables shall be put in an ignored build/ directory
# Ignore dynamic, static libs and libtool archive files
build/
*.so
*.so.*
*.dylib
*.a
*.la
*.exe
*.dll
*.exe
*.out

# Nim
# -----------------------------------------------------------------------------------------
nimcache/

# Rust
# -----------------------------------------------------------------------------------------

# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# Sage
# -----------------------------------------------------------------------------------------
*.sage.py

# Tests
test_*.txt
# Swap or debug
# -----------------------------------------------------------------------------------------
*.swp
*~

# Perf artifacts
# -----------------------------------------------------------------------------------------
perf.data
perf.data.old
23 changes: 23 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[workspace]
resolver = "2"
members = [
"constantine-rust/constantine-sys",
"constantine-rust/ctt-curve-bls12-381",
"constantine-rust/ctt-curve-bn254-snarks",
"constantine-rust/ctt-curve-pasta",
"constantine-rust/ctt-proto-ethereum-bls-signatures",
]

# The Nim static library is compiled with ThinLTO, always enable it

[profile.dev]
lto = "thin"

[profile.test]
lto = "thin"

[profile.release]
lto = "thin"

[profile.bench]
lto = "thin"
File renamed without changes.
File renamed without changes.
65 changes: 12 additions & 53 deletions bindings_generators/gen_header.nim → bindings/c_typedefs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ proc genHeaderLicense*(): string =
*/
"""

proc genHeader*(name, body: string): string =
proc genHeaderGuardAndInclude*(name, body: string): string =
&"""
#ifndef __CTT_H_{name}__
#define __CTT_H_{name}__
#include "constantine/core/datatypes.h"
{body}
#endif
Expand Down Expand Up @@ -70,9 +72,9 @@ typedef __UINT64_TYPE__ uint64_t;
#endif
#if defined(__STDC_VERSION__) && __STDC_VERSION__>=199901
# define bool _Bool
# define ctt_bool _Bool
#else
# define bool unsigned char
# define ctt_bool unsigned char
#endif
"""

Expand All @@ -85,12 +87,12 @@ typedef uint8_t byte;

proc genWordsRequired*(): string =
"""
#define WordBitWidth (sizeof(secret_word)*8)
#define words_required(bits) ((bits+WordBitWidth-1)/WordBitWidth)
#define CTT_WORD_BITWIDTH (sizeof(secret_word)*8)
#define CTT_WORDS_REQUIRED(bits) ((bits+WordBitWidth-1)/WordBitWidth)
"""

proc genField*(name: string, bits: int): string =
&"typedef struct {{ secret_word limbs[words_required({bits})]; }} {name};"
&"typedef struct {{ secret_word limbs[CTT_WORDS_REQUIRED({bits})]; }} {name};"

proc genExtField*(name: string, degree: int, basename: string): string =
&"typedef struct {{ {basename} c[{degree}]; }} {name};"
Expand Down Expand Up @@ -121,20 +123,20 @@ void ctt_{libName}_init_NimMain(void);"""
# -------------------------------------------

let TypeMap {.compileTime.} = newStringTable({
"bool": "bool",
"bool": "ctt_bool ",
"SecretBool": "secret_bool",
"SecretWord": "secret_word"
})

proc toCrettype(node: NimNode): string =
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 =
proc toCtrivialParam*(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkSym})

let isVar = typ.kind == nnkVarTy
Expand All @@ -151,7 +153,7 @@ proc toCtrivialParam(name: string, typ: NimNode): string =
# Pass-by-reference
constify & sTyp & "* " & name

proc toCparam(name: string, typ: NimNode): string =
proc toCparam*(name: string, typ: NimNode): string =
typ.expectKind({nnkVarTy, nnkCall, nnkSym})

if typ.kind == nnkCall:
Expand All @@ -174,46 +176,3 @@ proc toCparam(name: string, typ: NimNode): string =
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)

if fnDef.params[0].eqIdent"bool":
cBindings &= ") __attribute__((warn_unused_result));"
else:
cBindings &= ");"

if defined(CTT_GENERATE_HEADERS):
result = newConstStmt(cBindingsStr, newLit cBindings)
else:
result = body
21 changes: 21 additions & 0 deletions bindings/lib_constantine.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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.

# ############################################################
#
# Constantine library
#
# ############################################################

{.push warning[UnusedImport]: off.}

import
./lib_hashes,
./lib_curves,
# Protocols
../constantine/ethereum_bls_signatures
Loading

0 comments on commit 3e27f1e

Please sign in to comment.