Add Nim bindings (#176)

This commit is contained in:
andri lim 2023-03-08 20:04:30 +07:00 committed by GitHub
parent b71746df74
commit e8ed621dc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1124 additions and 0 deletions

58
.github/workflows/nim-bindings-test.yml vendored Normal file
View File

@ -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

View File

@ -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) |

4
bindings/nim/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/build
nimble.develop
nimble.paths
*.exe

41
bindings/nim/README.md Normal file
View File

@ -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).

14
bindings/nim/config.nims Normal file
View File

@ -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"

252
bindings/nim/kzg.nim Normal file
View File

@ -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 .}

125
bindings/nim/kzg_abi.nim Normal file
View File

@ -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.}

136
bindings/nim/kzg_ex.nim Normal file
View File

@ -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 .}

View File

@ -0,0 +1,3 @@
--styleCheck:usages
# nimyaml style will error
--styleCheck:hint

View File

@ -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)

View File

@ -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.".}

View File

@ -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])

View File

@ -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])

View File

@ -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