Add Nim bindings (#176)
This commit is contained in:
parent
b71746df74
commit
e8ed621dc8
|
@ -0,0 +1,58 @@
|
|||
name: Nim bindings tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
- windows-latest
|
||||
- macOS-latest
|
||||
nim-version:
|
||||
- stable
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Cache choosenim
|
||||
id: cache-choosenim
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.choosenim
|
||||
key: ${{ runner.os }}-choosenim-${{ matrix.nim-version}}
|
||||
|
||||
- name: Cache nimble
|
||||
id: cache-nimble
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.nimble
|
||||
key: ${{ runner.os }}-nimble-${{ matrix.nim-version}}-${{ hashFiles('bindings/nim/kzg_abi.nim') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nimble-${{ matrix.nim-version }}
|
||||
|
||||
- name: Setup nim
|
||||
uses: jiro4989/setup-nim-action@v1
|
||||
with:
|
||||
nim-version: ${{ matrix.nim-version }}
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
nimble install -y stew
|
||||
nimble install -y unittest2
|
||||
nimble install -y yaml
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd bindings/nim
|
||||
nim test
|
|
@ -32,6 +32,7 @@ There are bindings for the following languages:
|
|||
| C# | [README](bindings/csharp/README.md) |
|
||||
| Go | [README](bindings/go/README.md) |
|
||||
| Java | [README](bindings/java/README.md) |
|
||||
| Nim | [README](bindings/nim/README.md) |
|
||||
| Node.js | [README](bindings/node.js/README.md) |
|
||||
| Python | [README](bindings/python/README.md) |
|
||||
| Rust | [README](bindings/rust/README.md) |
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
/build
|
||||
nimble.develop
|
||||
nimble.paths
|
||||
*.exe
|
|
@ -0,0 +1,41 @@
|
|||
# Nim bindings
|
||||
|
||||
This directory contains Nim bindings for the c-kzg-4844 library.
|
||||
|
||||
## Requirements
|
||||
|
||||
This bindings support Nim compiler version 1.2, 1.4, 1.6, and devel.
|
||||
|
||||
You also need to install dependencies:
|
||||
|
||||
```
|
||||
nimble install stew
|
||||
```
|
||||
|
||||
## Tests
|
||||
|
||||
Currently tests only support Nim compiler version 1.4, and 1.6 because of yaml library limitations.
|
||||
|
||||
Dependencies:
|
||||
|
||||
```
|
||||
nimble install unittest2
|
||||
nimble install yaml
|
||||
```
|
||||
|
||||
Run the tests from folder `bindings\nim`:
|
||||
|
||||
```
|
||||
nim test
|
||||
```
|
||||
|
||||
## How to use this bindings in your project
|
||||
|
||||
Because the structure of folders is not a normal Nim library, we suggest you to
|
||||
clone this repository in your project sub folder or submodule it.
|
||||
|
||||
Then you can import one of the binding file into your project.
|
||||
|
||||
## Library
|
||||
|
||||
The library which uses this binding is [nim-kzg4844](https://github.com/status-im/nim-kzg4844).
|
|
@ -0,0 +1,14 @@
|
|||
# Helper functions
|
||||
proc test(args, path: string) =
|
||||
if not dirExists "build":
|
||||
mkDir "build"
|
||||
exec "nim " & getEnv("TEST_LANG", "c") & " " & getEnv("NIMFLAGS") & " " & args &
|
||||
" --outdir:build -r -f --hints:off --warnings:off --skipParentCfg " & path
|
||||
|
||||
task test, "Run all tests":
|
||||
echo ">>>>>>>>>>>>>>>> Run tests in DEBUG mode <<<<<<<<<<<<<<<<"
|
||||
test "-d:debug", "tests/test_all"
|
||||
echo ">>>>>>>>>>>>>>>> Run tests in RELEASE mode <<<<<<<<<<<<<<<<"
|
||||
test "-d:release", "tests/test_all"
|
||||
echo ">>>>>>>>>>>>>>>> Run tests in RELEASE and THREADS ON mode <<<<<<<<<<<<<<<<"
|
||||
test "--threads:on -d:release", "tests/test_all"
|
|
@ -0,0 +1,252 @@
|
|||
############################################################
|
||||
# Main API, wrapper on top of C FFI
|
||||
############################################################
|
||||
|
||||
import
|
||||
std/[streams, strutils],
|
||||
stew/[results, byteutils],
|
||||
./kzg_abi
|
||||
|
||||
export
|
||||
results,
|
||||
kzg_abi
|
||||
|
||||
type
|
||||
KzgCtx* = ref object
|
||||
val: KzgSettings
|
||||
|
||||
KzgProofAndY* = object
|
||||
proof*: KzgProof
|
||||
y*: KzgBytes32
|
||||
|
||||
G1Data* = array[48, byte]
|
||||
G2Data* = array[96, byte]
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
##############################################################
|
||||
# Private helpers
|
||||
##############################################################
|
||||
|
||||
proc destroy*(x: KzgCtx) =
|
||||
free_trusted_setup(x.val)
|
||||
|
||||
proc newKzgCtx(): KzgCtx =
|
||||
# Nim finalizer is still broken(v1.6)
|
||||
# consider to call destroy directly
|
||||
new(result, destroy)
|
||||
|
||||
template getPtr(x: untyped): auto =
|
||||
when (NimMajor, NimMinor) <= (1,6):
|
||||
unsafeAddr(x)
|
||||
else:
|
||||
addr(x)
|
||||
|
||||
template verify(res: KZG_RET) =
|
||||
if res != KZG_OK:
|
||||
return err($res)
|
||||
|
||||
##############################################################
|
||||
# Public functions
|
||||
##############################################################
|
||||
|
||||
proc loadTrustedSetup*(input: File): Result[KzgCtx, string] =
|
||||
let
|
||||
ctx = newKzgCtx()
|
||||
res = load_trusted_setup_file(ctx.val, input)
|
||||
|
||||
verify(res)
|
||||
ok(ctx)
|
||||
|
||||
proc loadTrustedSetup*(fileName: string): Result[KzgCtx, string] =
|
||||
try:
|
||||
let file = open(fileName)
|
||||
result = file.loadTrustedSetup()
|
||||
file.close()
|
||||
except IOError as ex:
|
||||
return err(ex.msg)
|
||||
|
||||
proc loadTrustedSetup*(g1: openArray[G1Data],
|
||||
g2: openArray[G2Data]):
|
||||
Result[KzgCtx, string] =
|
||||
if g1.len == 0 or g2.len == 0:
|
||||
return err($KZG_BADARGS)
|
||||
|
||||
let
|
||||
ctx = newKzgCtx()
|
||||
res = load_trusted_setup(ctx.val,
|
||||
g1[0][0].getPtr,
|
||||
g1.len.csize_t,
|
||||
g2[0][0].getPtr,
|
||||
g2.len.csize_t)
|
||||
|
||||
verify(res)
|
||||
ok(ctx)
|
||||
|
||||
proc loadTrustedSetupFromString*(input: string): Result[KzgCtx, string] =
|
||||
const
|
||||
NumG2 = 65
|
||||
G1Len = G1Data.len
|
||||
G2Len = G2Data.len
|
||||
|
||||
var
|
||||
s = newStringStream(input)
|
||||
g1: array[FIELD_ELEMENTS_PER_BLOB, G1Data]
|
||||
g2: array[NumG2, G2Data]
|
||||
|
||||
try:
|
||||
let fieldElems = s.readLine().parseInt()
|
||||
if fieldElems != FIELD_ELEMENTS_PER_BLOB:
|
||||
return err("invalid field elemments per blob, expect $1, got $2" % [
|
||||
$FIELD_ELEMENTS_PER_BLOB, $fieldElems
|
||||
])
|
||||
let numG2 = s.readLine().parseInt()
|
||||
if numG2 != NumG2:
|
||||
return err("invalid number of G2, expect $1, got $2" % [
|
||||
$NumG2, $numG2
|
||||
])
|
||||
|
||||
for i in 0 ..< FIELD_ELEMENTS_PER_BLOB:
|
||||
g1[i] = hexToByteArray[G1Len](s.readLine())
|
||||
|
||||
for i in 0 ..< NumG2:
|
||||
g2[i] = hexToByteArray[G2Len](s.readLine())
|
||||
except ValueError as ex:
|
||||
return err(ex.msg)
|
||||
except OSError as ex:
|
||||
return err(ex.msg)
|
||||
except IOError as ex:
|
||||
return err(ex.msg)
|
||||
|
||||
loadTrustedSetup(g1, g2)
|
||||
|
||||
proc toCommitment*(ctx: KzgCtx,
|
||||
blob: KzgBlob):
|
||||
Result[KzgCommitment, string] {.gcsafe.} =
|
||||
var kate: KzgCommitment
|
||||
let res = blob_to_kzg_commitment(kate, blob, ctx.val)
|
||||
verify(res)
|
||||
ok(kate)
|
||||
|
||||
proc computeProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob,
|
||||
z: KzgBytes32): Result[KzgProofAndY, string] {.gcsafe.} =
|
||||
var x: KzgProofAndY
|
||||
let res = compute_kzg_proof(
|
||||
x.proof,
|
||||
x.y,
|
||||
blob,
|
||||
z,
|
||||
ctx.val)
|
||||
verify(res)
|
||||
ok(x)
|
||||
|
||||
proc computeProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob,
|
||||
commitmentBytes: KzgBytes48): Result[KzgProof, string] {.gcsafe.} =
|
||||
var proof: KzgProof
|
||||
let res = compute_blob_kzg_proof(
|
||||
proof,
|
||||
blob,
|
||||
commitmentBytes,
|
||||
ctx.val)
|
||||
verify(res)
|
||||
ok(proof)
|
||||
|
||||
proc verifyProof*(ctx: KzgCtx,
|
||||
commitment: KzgBytes48,
|
||||
z: KzgBytes32, # Input Point
|
||||
y: KzgBytes32, # Claimed Value
|
||||
proof: KzgBytes48): Result[bool, string] {.gcsafe.} =
|
||||
var valid: bool
|
||||
let res = verify_kzg_proof(
|
||||
valid,
|
||||
commitment,
|
||||
z,
|
||||
y,
|
||||
proof,
|
||||
ctx.val)
|
||||
verify(res)
|
||||
ok(valid)
|
||||
|
||||
proc verifyProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob,
|
||||
commitment: KzgBytes48,
|
||||
proof: KzgBytes48): Result[bool, string] {.gcsafe.} =
|
||||
var valid: bool
|
||||
let res = verify_blob_kzg_proof(
|
||||
valid,
|
||||
blob,
|
||||
commitment,
|
||||
proof,
|
||||
ctx.val)
|
||||
verify(res)
|
||||
ok(valid)
|
||||
|
||||
proc verifyProofs*(ctx: KzgCtx,
|
||||
blobs: openArray[KzgBlob],
|
||||
commitments: openArray[KzgBytes48],
|
||||
proofs: openArray[KzgBytes48]): Result[bool, string] {.gcsafe.} =
|
||||
if blobs.len != commitments.len:
|
||||
return err($KZG_BADARGS)
|
||||
|
||||
if blobs.len != proofs.len:
|
||||
return err($KZG_BADARGS)
|
||||
|
||||
if blobs.len == 0:
|
||||
return ok(true)
|
||||
|
||||
var valid: bool
|
||||
let res = verify_blob_kzg_proof_batch(
|
||||
valid,
|
||||
blobs[0].getPtr,
|
||||
commitments[0].getPtr,
|
||||
proofs[0].getPtr,
|
||||
blobs.len.csize_t,
|
||||
ctx.val)
|
||||
verify(res)
|
||||
ok(valid)
|
||||
|
||||
##############################################################
|
||||
# Zero overhead aliases that match the spec
|
||||
##############################################################
|
||||
|
||||
template loadTrustedSetupFile*(input: File | string): untyped =
|
||||
loadTrustedSetup(input)
|
||||
|
||||
template blobToKzgCommitment*(ctx: KzgCtx,
|
||||
blob: KzgBlob): untyped =
|
||||
toCommitment(ctx, blob)
|
||||
|
||||
template computeKzgProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob, z: KzgBytes32): untyped =
|
||||
computeProof(ctx, blob, z)
|
||||
|
||||
template computeBlobKzgProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob,
|
||||
commitmentBytes: KzgBytes48): untyped =
|
||||
computeProof(ctx, blob, commitmentBytes)
|
||||
|
||||
template verifyKzgProof*(ctx: KzgCtx,
|
||||
commitment: KzgBytes48,
|
||||
z: KzgBytes32, # Input Point
|
||||
y: KzgBytes32, # Claimed Value
|
||||
proof: KzgBytes48): untyped =
|
||||
verifyProof(ctx, commitment, z, y, proof)
|
||||
|
||||
template verifyBlobKzgProof*(ctx: KzgCtx,
|
||||
blob: KzgBlob,
|
||||
commitment: KzgBytes48,
|
||||
proof: KzgBytes48): untyped =
|
||||
verifyProof(ctx, blob, commitment, proof)
|
||||
|
||||
template verifyBlobKzgProofBatch*(ctx: KzgCtx,
|
||||
blobs: openArray[KzgBlob],
|
||||
commitments: openArray[KzgBytes48],
|
||||
proofs: openArray[KzgBytes48]): untyped =
|
||||
verifyProofs(ctx, blobs, commitments, proofs)
|
||||
|
||||
{. pop .}
|
|
@ -0,0 +1,125 @@
|
|||
############################################################
|
||||
# FFI to C functions
|
||||
############################################################
|
||||
|
||||
import
|
||||
std/[strformat, strutils]
|
||||
|
||||
from os import DirSep
|
||||
|
||||
const
|
||||
# FIELD_ELEMENTS_PER_BLOB is overrideable from
|
||||
# compiler switch -d: or --define:
|
||||
FIELD_ELEMENTS_PER_BLOB* {.strdefine.} = 4096
|
||||
# kzgPath: c-kzg-4844 project path, removing 3 last elem
|
||||
kzgPath = currentSourcePath.rsplit(DirSep, 3)[0] & "/"
|
||||
blstPath = kzgPath & "blst/"
|
||||
srcPath = kzgPath & "src/"
|
||||
bindingsPath = blstPath & "bindings"
|
||||
|
||||
when not defined(kzgExternalBlst):
|
||||
# Use default blst shipped with c-kzg-4844
|
||||
{.compile: blstPath & "build/assembly.S".}
|
||||
{.compile: blstPath & "src/server.c"}
|
||||
|
||||
{.compile: srcPath & "c_kzg_4844.c"}
|
||||
|
||||
{.passc: "-I" & bindingsPath &
|
||||
" -DFIELD_ELEMENTS_PER_BLOB=" &
|
||||
fmt"{FIELD_ELEMENTS_PER_BLOB}".}
|
||||
{.passc: "-I" & srcPath .}
|
||||
|
||||
const
|
||||
BYTES_PER_FIELD_ELEMENT* = 32
|
||||
KzgBlobSize* = FIELD_ELEMENTS_PER_BLOB*BYTES_PER_FIELD_ELEMENT
|
||||
|
||||
type
|
||||
KZG_RET* = distinct cint
|
||||
|
||||
const
|
||||
# The common return type for all routines in which something can go wrong.
|
||||
KZG_OK* = (0).KZG_RET
|
||||
KZG_BADARGS* = (1).KZG_RET
|
||||
KZG_ERROR* = (2).KZG_RET
|
||||
KZG_MALLOC* = (3).KZG_RET
|
||||
|
||||
proc `$`*(x: KZG_RET): string =
|
||||
case x
|
||||
of KZG_OK: "ok"
|
||||
of KZG_BADARGS: "kzg badargs"
|
||||
of KZG_ERROR: "kzg error"
|
||||
of KZG_MALLOC: "kzg malloc error"
|
||||
else: "kzg unknown error"
|
||||
|
||||
proc `==`*(a, b: KZG_RET): bool =
|
||||
a.cint == b.cint
|
||||
|
||||
type
|
||||
# Stores the setup and parameters needed for performing FFTs.
|
||||
KzgSettings* {.importc: "KZGSettings",
|
||||
header: "c_kzg_4844.h", byref.} = object
|
||||
|
||||
# A basic blob data.
|
||||
KzgBlob* = array[KzgBlobSize, byte]
|
||||
|
||||
# An array of 48 bytes. Represents an untrusted
|
||||
# (potentially invalid) commitment/proof.
|
||||
KzgBytes48* = array[48, byte]
|
||||
|
||||
# An array of 32 bytes. Represents an untrusted
|
||||
# (potentially invalid) field element.
|
||||
KzgBytes32* = array[32, byte]
|
||||
|
||||
# A trusted (valid) KZG commitment.
|
||||
KzgCommitment* = KzgBytes48
|
||||
|
||||
# A trusted (valid) KZG proof.
|
||||
KzgProof* = KzgBytes48
|
||||
|
||||
{.pragma: kzg_abi, importc, cdecl, header: "c_kzg_4844.h".}
|
||||
|
||||
proc load_trusted_setup*(res: KzgSettings,
|
||||
g1Bytes: ptr byte, # n1 * 48 bytes
|
||||
n1: csize_t,
|
||||
g2Bytes: ptr byte, # n2 * 96 bytes
|
||||
n2: csize_t): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc load_trusted_setup_file*(res: KzgSettings,
|
||||
input: File): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc free_trusted_setup*(s: KzgSettings) {.kzg_abi.}
|
||||
|
||||
proc blob_to_kzg_commitment*(res: var KzgCommitment,
|
||||
blob: KzgBlob,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc compute_kzg_proof*(res: var KzgProof,
|
||||
yOut: var KzgBytes32,
|
||||
blob: KzgBlob,
|
||||
zBytes: KzgBytes32,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc compute_blob_kzg_proof*(res: var KzgProof,
|
||||
blob: KzgBlob,
|
||||
commitmentBytes: KzgBytes48,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc verify_kzg_proof*(res: var bool,
|
||||
commitmentBytes: KzgBytes48,
|
||||
zBytes: KzgBytes32,
|
||||
yBytes: KzgBytes32,
|
||||
proofBytes: KzgBytes48,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc verify_blob_kzg_proof*(res: var bool,
|
||||
blob: KzgBlob,
|
||||
commitmentsBytes: KzgBytes48,
|
||||
proofBytes: KzgBytes48,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
||||
|
||||
proc verify_blob_kzg_proof_batch*(res: var bool,
|
||||
blobs: ptr KzgBlob,
|
||||
commitmentsBytes: ptr KzgBytes48,
|
||||
proofBytes: ptr KzgBytes48,
|
||||
n: csize_t,
|
||||
s: KzgSettings): KZG_RET {.kzg_abi.}
|
|
@ -0,0 +1,136 @@
|
|||
############################################################
|
||||
# Convenience wrapper where KzgSettings is a global variable
|
||||
############################################################
|
||||
|
||||
import
|
||||
stew/results,
|
||||
./kzg
|
||||
|
||||
export
|
||||
results,
|
||||
kzg
|
||||
|
||||
type
|
||||
Kzg* = object
|
||||
|
||||
when (NimMajor, NimMinor) < (1, 4):
|
||||
{.push raises: [Defect].}
|
||||
else:
|
||||
{.push raises: [].}
|
||||
|
||||
##############################################################
|
||||
# Private helpers
|
||||
##############################################################
|
||||
|
||||
var gCtx: KzgCtx
|
||||
|
||||
const
|
||||
GlobalCtxErr = "kzg global context not loaded"
|
||||
|
||||
template setupCtx(body: untyped): untyped =
|
||||
let res = body
|
||||
if res.isErr:
|
||||
return err(res.error)
|
||||
gCtx = res.get
|
||||
ok()
|
||||
|
||||
template verifyCtx(body: untyped): untyped =
|
||||
{.gcsafe.}:
|
||||
if gCtx.isNil:
|
||||
return err(GlobalCtxErr)
|
||||
body
|
||||
|
||||
##############################################################
|
||||
# Public functions
|
||||
##############################################################
|
||||
|
||||
proc loadTrustedSetup*(_: type Kzg,
|
||||
input: File): Result[void, string] =
|
||||
setupCtx:
|
||||
kzg.loadTrustedSetup(input)
|
||||
|
||||
proc loadTrustedSetup*(_: type Kzg,
|
||||
fileName: string): Result[void, string] =
|
||||
setupCtx:
|
||||
kzg.loadTrustedSetup(fileName)
|
||||
|
||||
proc loadTrustedSetup*(_: type Kzg, g1: openArray[G1Data],
|
||||
g2: openArray[G2Data]):
|
||||
Result[void, string] =
|
||||
setupCtx:
|
||||
kzg.loadTrustedSetup(g1, g2)
|
||||
|
||||
proc loadTrustedSetupFromString*(_: type Kzg,
|
||||
input: string): Result[void, string] =
|
||||
setupCtx:
|
||||
kzg.loadTrustedSetupFromString(input)
|
||||
|
||||
proc toCommitment*(blob: KzgBlob):
|
||||
Result[KzgCommitment, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.toCommitment(blob)
|
||||
|
||||
proc computeProof*(blob: KzgBlob,
|
||||
z: KzgBytes32): Result[KzgProofAndY, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.computeProof(blob, z)
|
||||
|
||||
proc computeProof*(blob: KzgBlob,
|
||||
commitmentBytes: KzgBytes48):
|
||||
Result[KzgProof, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.computeProof(blob, commitmentBytes)
|
||||
|
||||
proc verifyProof*(commitment: KzgBytes48,
|
||||
z: KzgBytes32, # Input Point
|
||||
y: KzgBytes32, # Claimed Value
|
||||
proof: KzgBytes48): Result[bool, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.verifyProof(commitment, z, y, proof)
|
||||
|
||||
proc verifyProof*(blob: KzgBlob,
|
||||
commitment: KzgBytes48,
|
||||
proof: KzgBytes48): Result[bool, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.verifyProof(blob, commitment, proof)
|
||||
|
||||
proc verifyProofs*(blobs: openArray[KzgBlob],
|
||||
commitments: openArray[KzgBytes48],
|
||||
proofs: openArray[KzgBytes48]): Result[bool, string] {.gcsafe.} =
|
||||
verifyCtx:
|
||||
gCtx.verifyProofs(blobs, commitments, proofs)
|
||||
|
||||
##############################################################
|
||||
# Zero overhead aliases that match the spec
|
||||
##############################################################
|
||||
|
||||
template loadTrustedSetupFile*(T: type Kzg, input: File | string): untyped =
|
||||
loadTrustedSetup(T, input)
|
||||
|
||||
template blobToKzgCommitment*(blob: KzgBlob): untyped =
|
||||
toCommitment(blob)
|
||||
|
||||
template computeKzgProof*(blob: KzgBlob, z: KzgBytes32): untyped =
|
||||
computeProof(blob, z)
|
||||
|
||||
template computeBlobKzgProof*(blob: KzgBlob,
|
||||
commitmentBytes: KzgBytes48): untyped =
|
||||
computeProof(blob, commitmentBytes)
|
||||
|
||||
template verifyKzgProof*(commitment: KzgBytes48,
|
||||
z: KzgBytes32, # Input Point
|
||||
y: KzgBytes32, # Claimed Value
|
||||
proof: KzgBytes48): untyped =
|
||||
verifyProof(commitment, z, y, proof)
|
||||
|
||||
template verifyBlobKzgProof*(blob: KzgBlob,
|
||||
commitment: KzgBytes48,
|
||||
proof: KzgBytes48): untyped =
|
||||
verifyProof(blob, commitment, proof)
|
||||
|
||||
template verifyBlobKzgProofBatch*(blobs: openArray[KzgBlob],
|
||||
commitments: openArray[KzgBytes48],
|
||||
proofs: openArray[KzgBytes48]): untyped =
|
||||
verifyProofs(blobs, commitments, proofs)
|
||||
|
||||
{. pop .}
|
|
@ -0,0 +1,3 @@
|
|||
--styleCheck:usages
|
||||
# nimyaml style will error
|
||||
--styleCheck:hint
|
|
@ -0,0 +1,131 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
std/[streams, strutils],
|
||||
unittest2,
|
||||
stew/byteutils,
|
||||
../kzg_abi,
|
||||
./types
|
||||
|
||||
proc readSetup(): KzgSettings =
|
||||
var
|
||||
s = newFileStream(trustedSetupFile)
|
||||
g1Bytes: array[FIELD_ELEMENTS_PER_BLOB * 48, byte]
|
||||
g2Bytes: array[65 * 96, byte]
|
||||
|
||||
doAssert(s.isNil.not,
|
||||
"FAILED TO OPEN: " & trustedSetupFile)
|
||||
|
||||
let fieldElems = s.readLine().parseInt()
|
||||
doAssert fieldElems == FIELD_ELEMENTS_PER_BLOB
|
||||
let numG2 = s.readLine().parseInt()
|
||||
doAssert numG2 == 65
|
||||
|
||||
for i in 0 ..< FIELD_ELEMENTS_PER_BLOB:
|
||||
let z = hexToByteArray[48](s.readLine())
|
||||
g1Bytes[i*48 ..< i*48+48] = z[0..<48]
|
||||
|
||||
for i in 0 ..< 65:
|
||||
let z = hexToByteArray[96](s.readLine())
|
||||
g2Bytes[i*96 ..< i*96+96] = z[0..<96]
|
||||
|
||||
let res = load_trusted_setup(result,
|
||||
g1Bytes[0].addr, FIELD_ELEMENTS_PER_BLOB,
|
||||
g2Bytes[0].addr, 65)
|
||||
|
||||
doAssert(res == KZG_OK,
|
||||
"ERROR: " & $res)
|
||||
|
||||
proc readSetup(filename: string): KzgSettings =
|
||||
var file = open(filename)
|
||||
let ret = load_trusted_setup_file(result, file)
|
||||
doAssert ret == KZG_OK
|
||||
file.close()
|
||||
|
||||
proc createKateBlobs(s: KzgSettings, n: int): KateBlobs =
|
||||
for i in 0..<n:
|
||||
var blob: KzgBlob
|
||||
discard urandom(blob)
|
||||
for i in 0..<len(blob):
|
||||
# don't overflow modulus
|
||||
if blob[i] > MAX_TOP_BYTE and i %% BYTES_PER_FIELD_ELEMENT == 31:
|
||||
blob[i] = MAX_TOP_BYTE
|
||||
result.blobs.add(blob)
|
||||
|
||||
for i in 0..<n:
|
||||
var kate: KzgCommitment
|
||||
doAssert blob_to_kzg_commitment(kate, result.blobs[i], s) == KZG_OK
|
||||
result.kates.add(kate)
|
||||
|
||||
let
|
||||
kzgs = readSetup()
|
||||
|
||||
suite "verify proof (abi)":
|
||||
let
|
||||
settings = readSetup(trustedSetupFile)
|
||||
|
||||
test "verify batch proof success":
|
||||
var kb = kzgs.createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
|
||||
for i in 0..<nblobs:
|
||||
let res = compute_blob_kzg_proof(kp[i], kb.blobs[i], kb.kates[i], kzgs)
|
||||
check res == KZG_OK
|
||||
|
||||
var ok: bool
|
||||
let res = verify_blob_kzg_proof_batch(ok,
|
||||
kb.blobs[0].addr,
|
||||
kb.kates[0].addr,
|
||||
kp[0].addr,
|
||||
csize_t(nblobs),
|
||||
kzgs)
|
||||
check res == KZG_OK
|
||||
check ok
|
||||
|
||||
test "verify batch proof failure":
|
||||
var kb = kzgs.createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
|
||||
for i in 0..<nblobs:
|
||||
let res = compute_blob_kzg_proof(kp[i], kb.blobs[i], kb.kates[i], kzgs)
|
||||
check res == KZG_OK
|
||||
|
||||
var other = kzgs.createKateBlobs(nblobs)
|
||||
for i in 0..<nblobs:
|
||||
let res = compute_blob_kzg_proof(kp[i], other.blobs[i], other.kates[i], kzgs)
|
||||
check res == KZG_OK
|
||||
|
||||
var ok: bool
|
||||
let res = verify_blob_kzg_proof_batch(ok,
|
||||
kb.blobs[0].addr,
|
||||
kb.kates[0].addr,
|
||||
kp[0].addr,
|
||||
csize_t(nblobs),
|
||||
kzgs)
|
||||
check res == KZG_OK
|
||||
check ok == false
|
||||
|
||||
test "verify blob proof":
|
||||
var kp: KzgProof
|
||||
var res = compute_blob_kzg_proof(kp, blob, commitment, kzgs)
|
||||
check res == KZG_OK
|
||||
|
||||
var ok: bool
|
||||
res = verify_blob_kzg_proof(ok, blob, commitment, kp, kzgs)
|
||||
check res == KZG_OK
|
||||
check ok
|
||||
|
||||
test "verify proof":
|
||||
var kp: KzgProof
|
||||
var ky: KzgBytes32
|
||||
var res = compute_kzg_proof(kp, ky, blob, inputPoint, kzgs)
|
||||
check res == KZG_OK
|
||||
check kp == proof
|
||||
check ky == claimedValue
|
||||
|
||||
var ok: bool
|
||||
res = verify_kzg_proof(ok, commitment, inputPoint, claimedValue, kp, kzgs)
|
||||
check res == KZG_OK
|
||||
check ok
|
||||
|
||||
free_trusted_setup(settings)
|
|
@ -0,0 +1,14 @@
|
|||
import
|
||||
test_abi,
|
||||
test_kzg,
|
||||
test_kzg_ex
|
||||
|
||||
when (NimMajor, NimMinor) >= (1, 4) and
|
||||
(NimMajor, NimMinor) <= (1, 6):
|
||||
# nim devel causes shallowCopy error
|
||||
# on yaml
|
||||
import
|
||||
test_yaml
|
||||
else:
|
||||
{.warning: "test_yaml skipped because Nim version " &
|
||||
$NimMajor & "." & $NimMinor & " currently not supported.".}
|
|
@ -0,0 +1,93 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
unittest2,
|
||||
../kzg,
|
||||
./types
|
||||
|
||||
proc createKateBlobs(ctx: KzgCtx, n: int): KateBlobs =
|
||||
var blob: KzgBlob
|
||||
for i in 0..<n:
|
||||
discard urandom(blob)
|
||||
for i in 0..<len(blob):
|
||||
# don't overflow modulus
|
||||
if blob[i] > MAX_TOP_BYTE and i %% BYTES_PER_FIELD_ELEMENT == 31:
|
||||
blob[i] = MAX_TOP_BYTE
|
||||
result.blobs.add(blob)
|
||||
|
||||
for i in 0..<n:
|
||||
let res = ctx.toCommitment(result.blobs[i])
|
||||
doAssert res.isOk
|
||||
result.kates.add(res.get)
|
||||
|
||||
suite "verify proof (high-level)":
|
||||
var ctx: KzgCtx
|
||||
|
||||
test "load trusted setup from string":
|
||||
let res = loadTrustedSetupFromString(trustedSetup)
|
||||
check res.isOk
|
||||
ctx = res.get
|
||||
|
||||
test "verify batch proof success":
|
||||
let kb = ctx.createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = ctx.computeProof(kb.blobs[i], kb.kates[i])
|
||||
check pres.isOk
|
||||
kp[i] = pres.get
|
||||
|
||||
let res = ctx.verifyProofs(kb.blobs, kb.kates, kp)
|
||||
check res.isOk
|
||||
check res.get == true
|
||||
|
||||
test "verify batch proof failure":
|
||||
let kb = ctx.createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = ctx.computeProof(kb.blobs[i], kb.kates[i])
|
||||
check pres.isOk
|
||||
kp[i] = pres.get
|
||||
|
||||
let other = ctx.createKateBlobs(nblobs)
|
||||
var badProofs: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = ctx.computeProof(other.blobs[i], other.kates[i])
|
||||
check pres.isOk
|
||||
badProofs[i] = pres.get
|
||||
|
||||
let res = ctx.verifyProofs(kb.blobs, kb.kates, badProofs)
|
||||
check res.isOk
|
||||
check res.get == false
|
||||
|
||||
test "verify blob proof":
|
||||
let kp = ctx.computeProof(blob, commitment)
|
||||
check kp.isOk
|
||||
|
||||
let res = ctx.verifyProof(blob, commitment, kp.get)
|
||||
check res.isOk
|
||||
|
||||
test "verify proof":
|
||||
let kp = ctx.computeProof(blob, inputPoint)
|
||||
check kp.isOk
|
||||
check kp.get.proof == proof
|
||||
check kp.get.y == claimedValue
|
||||
|
||||
let res = ctx.verifyProof(commitment, inputPoint, claimedValue, kp.get.proof)
|
||||
check res.isOk
|
||||
|
||||
test "template aliases":
|
||||
# no need to check return value
|
||||
# only test if those templates can be compiled succesfully
|
||||
let res = loadTrustedSetupFile(trustedSetupFile)
|
||||
check res.isOk
|
||||
ctx = res.get
|
||||
|
||||
discard ctx.blobToKzgCommitment(blob)
|
||||
let kp = ctx.computeKzgProof(blob, inputPoint)
|
||||
discard ctx.computeBlobKzgProof(blob, commitment)
|
||||
discard ctx.verifyKzgProof(commitment, inputPoint, claimedValue, kp.get.proof)
|
||||
discard ctx.verifyBlobKzgProof(blob, commitment, proof)
|
||||
|
||||
let kb = ctx.createKateBlobs(1)
|
||||
discard ctx.verifyBlobKzgProofBatch(kb.blobs, kb.kates, [kp.get.proof])
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
unittest2,
|
||||
../kzg_ex,
|
||||
./types
|
||||
|
||||
proc createKateBlobs(n: int): KateBlobs =
|
||||
var blob: KzgBlob
|
||||
for i in 0..<n:
|
||||
discard urandom(blob)
|
||||
for i in 0..<len(blob):
|
||||
# don't overflow modulus
|
||||
if blob[i] > MAX_TOP_BYTE and i %% BYTES_PER_FIELD_ELEMENT == 31:
|
||||
blob[i] = MAX_TOP_BYTE
|
||||
result.blobs.add(blob)
|
||||
|
||||
for i in 0..<n:
|
||||
let res = toCommitment(result.blobs[i])
|
||||
doAssert res.isOk
|
||||
result.kates.add(res.get)
|
||||
|
||||
suite "verify proof (extended version)":
|
||||
test "load trusted setup from string":
|
||||
let res = Kzg.loadTrustedSetupFromString(trustedSetup)
|
||||
check res.isOk
|
||||
|
||||
test "verify batch proof success":
|
||||
let kb = createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = computeProof(kb.blobs[i], kb.kates[i])
|
||||
check pres.isOk
|
||||
kp[i] = pres.get
|
||||
|
||||
let res = verifyProofs(kb.blobs, kb.kates, kp)
|
||||
check res.isOk
|
||||
check res.get == true
|
||||
|
||||
test "verify batch proof failure":
|
||||
let kb = createKateBlobs(nblobs)
|
||||
var kp: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = computeProof(kb.blobs[i], kb.kates[i])
|
||||
check pres.isOk
|
||||
kp[i] = pres.get
|
||||
|
||||
let other = createKateBlobs(nblobs)
|
||||
var badProofs: array[nblobs, KzgProof]
|
||||
for i in 0..<nblobs:
|
||||
let pres = computeProof(other.blobs[i], other.kates[i])
|
||||
check pres.isOk
|
||||
badProofs[i] = pres.get
|
||||
|
||||
let res = verifyProofs(kb.blobs, kb.kates, badProofs)
|
||||
check res.isOk
|
||||
check res.get == false
|
||||
|
||||
test "verify blob proof":
|
||||
let kp = computeProof(blob, commitment)
|
||||
check kp.isOk
|
||||
|
||||
let res = verifyProof(blob, commitment, kp.get)
|
||||
check res.isOk
|
||||
|
||||
test "verify proof":
|
||||
let kp = computeProof(blob, inputPoint)
|
||||
check kp.isOk
|
||||
check kp.get.proof == proof
|
||||
check kp.get.y == claimedValue
|
||||
|
||||
let res = verifyProof(commitment, inputPoint, claimedValue, kp.get.proof)
|
||||
check res.isOk
|
||||
|
||||
test "template aliases":
|
||||
# no need to check return value
|
||||
# only test if those templates can be compiled succesfully
|
||||
discard Kzg.loadTrustedSetupFile(trustedSetupFile)
|
||||
discard blobToKzgCommitment(blob)
|
||||
let kp = computeKzgProof(blob, inputPoint)
|
||||
discard computeBlobKzgProof(blob, commitment)
|
||||
discard verifyKzgProof(commitment, inputPoint, claimedValue, kp.get.proof)
|
||||
discard verifyBlobKzgProof(blob, commitment, proof)
|
||||
let kb = createKateBlobs(1)
|
||||
discard verifyBlobKzgProofBatch(kb.blobs, kb.kates, [kp.get.proof])
|
|
@ -0,0 +1,132 @@
|
|||
{.used.}
|
||||
|
||||
import
|
||||
std/[os, strutils, streams],
|
||||
unittest2, yaml,
|
||||
stew/byteutils,
|
||||
../kzg,
|
||||
./types
|
||||
|
||||
const
|
||||
testBase = kzgPath & "tests/"
|
||||
BLOB_TO_KZG_COMMITMENT_TESTS = testBase & "blob_to_kzg_commitment"
|
||||
COMPUTE_KZG_PROOF_TESTS = testBase & "compute_kzg_proof"
|
||||
COMPUTE_BLOB_KZG_PROOF_TESTS = testBase & "compute_blob_kzg_proof"
|
||||
VERIFY_KZG_PROOF_TESTS = testBase & "verify_kzg_proof"
|
||||
VERIFY_BLOB_KZG_PROOF_TESTS = testBase & "verify_blob_kzg_proof"
|
||||
VERIFY_BLOB_KZG_PROOF_BATCH_TESTS = testBase & "verify_blob_kzg_proof_batch"
|
||||
|
||||
proc toTestName(x: string): string =
|
||||
let parts = x.split(DirSep)
|
||||
parts[^2]
|
||||
|
||||
proc loadYaml(filename: string): YamlNode =
|
||||
var s = newFileStream(filename)
|
||||
load(s, result)
|
||||
s.close()
|
||||
|
||||
proc fromHex(T: type, x: string): T =
|
||||
if (x.len - 2) div 2 > sizeof(T):
|
||||
raise newException(ValueError, "invalid hex")
|
||||
hexToByteArray[sizeof(T)](x)
|
||||
|
||||
proc fromHex(T: type, x: YamlNode): T =
|
||||
T.fromHex(x.content)
|
||||
|
||||
proc fromHexList(T: type, xList: YamlNode): seq[T] =
|
||||
for x in xList:
|
||||
result.add(T.fromHex(x.content))
|
||||
|
||||
suite "yaml tests":
|
||||
var ctx: KzgCtx
|
||||
|
||||
test "load trusted setup from string":
|
||||
let res = loadTrustedSetupFromString(trustedSetup)
|
||||
check res.isOk
|
||||
ctx = res.get
|
||||
|
||||
for filename in walkDirRec(BLOB_TO_KZG_COMMITMENT_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
blob = KzgBlob.fromHex(n["input"]["blob"])
|
||||
res = ctx.toCommitment(blob)
|
||||
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
let kate = KzgCommitment.fromHex(n["output"])
|
||||
check kate == res.get
|
||||
|
||||
for filename in walkDirRec(COMPUTE_KZG_PROOF_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
blob = KzgBlob.fromHex(n["input"]["blob"])
|
||||
zBytes = KzgBytes32.fromHex(n["input"]["z"])
|
||||
res = ctx.computeProof(blob, zBytes)
|
||||
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
let proof = KzgProof.fromHex(n["output"][0])
|
||||
check proof == res.get.proof
|
||||
let y = KzgBytes32.fromHex(n["output"][1])
|
||||
check y == res.get.y
|
||||
|
||||
for filename in walkDirRec(COMPUTE_BLOB_KZG_PROOF_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
blob = KzgBlob.fromHex(n["input"]["blob"])
|
||||
commitment = KzgCommitment.fromHex(n["input"]["commitment"])
|
||||
res = ctx.computeProof(blob, commitment)
|
||||
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
let proof = KzgProof.fromHex(n["output"])
|
||||
check proof == res.get
|
||||
|
||||
for filename in walkDirRec(VERIFY_KZG_PROOF_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
commitment = KzgCommitment.fromHex(n["input"]["commitment"])
|
||||
z = KzgBytes32.fromHex(n["input"]["z"])
|
||||
y = KzgBytes32.fromHex(n["input"]["y"])
|
||||
proof = KzgProof.fromHex(n["input"]["proof"])
|
||||
|
||||
let res = ctx.verifyProof(commitment, z, y, proof)
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
check n["output"].content == $res.get
|
||||
|
||||
for filename in walkDirRec(VERIFY_BLOB_KZG_PROOF_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
blob = KzgBlob.fromHex(n["input"]["blob"])
|
||||
commitment = KzgCommitment.fromHex(n["input"]["commitment"])
|
||||
proof = KzgProof.fromHex(n["input"]["proof"])
|
||||
|
||||
let res = ctx.verifyProof(blob, commitment, proof)
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
check n["output"].content == $res.get
|
||||
|
||||
for filename in walkDirRec(VERIFY_BLOB_KZG_PROOF_BATCH_TESTS):
|
||||
test toTestName(filename):
|
||||
let
|
||||
n = loadYaml(filename)
|
||||
blobs = KzgBlob.fromHexList(n["input"]["blobs"])
|
||||
commitments = KzgCommitment.fromHexList(n["input"]["commitments"])
|
||||
proofs = KzgProof.fromHexList(n["input"]["proofs"])
|
||||
res = ctx.verifyProofs(blobs, commitments, proofs)
|
||||
|
||||
if res.isErr:
|
||||
check n["output"].content == "null"
|
||||
else:
|
||||
check n["output"].content == $res.get
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue