initial commit

This commit is contained in:
jangko 2023-02-09 01:41:29 +07:00
commit d0af4143e2
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
14 changed files with 773 additions and 0 deletions

162
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,162 @@
name: CI
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
jobs:
build:
strategy:
fail-fast: false
matrix:
target:
- os: linux
cpu: amd64
- os: linux
cpu: i386
- os: macos
cpu: amd64
- os: windows
cpu: amd64
- os: windows
cpu: i386
branch: [version-1-2, version-1-4, version-1-6, devel]
include:
- target:
os: linux
builder: ubuntu-20.04
shell: bash
- target:
os: macos
builder: macos-12
shell: bash
- target:
os: windows
builder: windows-2019
shell: msys2 {0}
defaults:
run:
shell: ${{ matrix.shell }}
name: '${{ matrix.target.os }}-${{ matrix.target.cpu }} (Nim ${{ matrix.branch }})'
runs-on: ${{ matrix.builder }}
steps:
- name: Checkout nim-kzg4844
uses: actions/checkout@v3
with:
submodules: true
- name: Install build dependencies (Linux i386)
if: runner.os == 'Linux' && matrix.target.cpu == 'i386'
run: |
sudo dpkg --add-architecture i386
sudo apt-fast update -qq
sudo DEBIAN_FRONTEND='noninteractive' apt-fast install \
--no-install-recommends -yq gcc-multilib g++-multilib \
libssl-dev:i386
mkdir -p external/bin
cat << EOF > external/bin/gcc
#!/bin/bash
exec $(which gcc) -m32 "\$@"
EOF
cat << EOF > external/bin/g++
#!/bin/bash
exec $(which g++) -m32 "\$@"
EOF
chmod 755 external/bin/gcc external/bin/g++
echo '${{ github.workspace }}/external/bin' >> $GITHUB_PATH
- name: MSYS2 (Windows i386)
if: runner.os == 'Windows' && matrix.target.cpu == 'i386'
uses: msys2/setup-msys2@v2
with:
path-type: inherit
msystem: MINGW32
install: >-
base-devel
git
mingw-w64-i686-toolchain
- name: MSYS2 (Windows amd64)
if: runner.os == 'Windows' && matrix.target.cpu == 'amd64'
uses: msys2/setup-msys2@v2
with:
path-type: inherit
install: >-
base-devel
git
mingw-w64-x86_64-toolchain
- name: Restore Nim DLLs dependencies (Windows) from cache
if: runner.os == 'Windows'
id: windows-dlls-cache
uses: actions/cache@v3
with:
path: external/dlls
key: 'dlls'
- name: Install DLL dependencies (Windows)
if: >
steps.windows-dlls-cache.outputs.cache-hit != 'true' &&
runner.os == 'Windows'
run: |
mkdir external
curl -L "https://nim-lang.org/download/windeps.zip" -o external/windeps.zip
7z x external/windeps.zip -oexternal/dlls
- name: Path to cached dependencies (Windows)
if: >
runner.os == 'Windows'
run: |
echo '${{ github.workspace }}'"/external/dlls" >> $GITHUB_PATH
- name: Derive environment variables
run: |
if [[ '${{ matrix.target.cpu }}' == 'amd64' ]]; then
PLATFORM=x64
else
PLATFORM=x86
fi
echo "PLATFORM=$PLATFORM" >> $GITHUB_ENV
ncpu=
MAKE_CMD="make"
case '${{ runner.os }}' in
'Linux')
ncpu=$(nproc)
;;
'macOS')
ncpu=$(sysctl -n hw.ncpu)
;;
'Windows')
ncpu=$NUMBER_OF_PROCESSORS
MAKE_CMD="mingw32-make"
;;
esac
[[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1
echo "ncpu=$ncpu" >> $GITHUB_ENV
echo "MAKE_CMD=${MAKE_CMD}" >> $GITHUB_ENV
- name: Build Nim and Nimble
run: |
curl -O -L -s -S https://raw.githubusercontent.com/status-im/nimbus-build-system/master/scripts/build_nim.sh
env MAKE="${MAKE_CMD} -j${ncpu}" ARCH_OVERRIDE=${PLATFORM} NIM_COMMIT=${{ matrix.branch }} \
QUICK_AND_DIRTY_COMPILER=1 QUICK_AND_DIRTY_NIMBLE=1 CC=gcc \
bash build_nim.sh nim csources dist/nimble NimBinaries
echo '${{ github.workspace }}/nim/bin' >> $GITHUB_PATH
- name: Run nim-kzg4844 tests
run: |
if [[ "${{ matrix.target.os }}" == "windows" ]]; then
# https://github.com/status-im/nimbus-eth2/issues/3121
export NIMFLAGS="-d:nimRawSetjmp"
fi
nim --version
nimble --version
nimble install -y --depsOnly
rm -f nimble.lock
env TEST_LANG="c" nimble test
env TEST_LANG="cpp" nimble test

4
.gitignore vendored Normal file
View File

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

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "kzg4844/csources"]
path = kzg4844/csources
url = https://github.com/ethereum/c-kzg-4844

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# kzg4844 wrapper in nim
![Github action](https://github.com/status-im/nim-kzg4844/workflows/CI/badge.svg)
This is a Nim wrapper of original C implementation of [c-kzg-4844](https://github.com/ethereum/c-kzg-4844)
## License
Licensed and distributed under either of
* MIT license: [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT
or
* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
at your option. These files may not be copied, modified, or distributed except according to those terms.

15
kzg4844.nim Normal file
View File

@ -0,0 +1,15 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
kzg4844/kzg
export
kzg

33
kzg4844.nimble Normal file
View File

@ -0,0 +1,33 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
mode = ScriptMode.Verbose
packageName = "kzg4844"
version = "0.1.0"
author = "Status Research & Development GmbH"
description = "c-kzg-4844 wrapper in Nim"
license = "Apache License 2.0"
skipDirs = @["tests"]
requires "nim >= 1.2.0"
requires "stew >= 0.1.0"
requires "unittest2"
# 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":
test "-d:debug", "tests/test_all"
test "-d:release", "tests/test_all"
test "--threads:on -d:release", "tests/test_all"

1
kzg4844/csources Submodule

@ -0,0 +1 @@
Subproject commit 5cfbc341352f12330aaf8e5f5c4df14dca829f1a

190
kzg4844/kzg.nim Normal file
View File

@ -0,0 +1,190 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
std/[streams, strutils],
kzg_abi,
stew/[results, byteutils]
export
results,
kzg_abi
type
KzgCtx* = ref object
val: KzgSettings
G1Data* = array[48, byte]
G2Data* = array[96, byte]
Bytes32 = array[32, 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 =
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] =
var
s = newStringStream(input)
g1: array[FIELD_ELEMENTS_PER_BLOB, G1Data]
g2: array[65, G2Data]
try:
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:
g1[i] = hexToByteArray[48](s.readLine())
for i in 0 ..< 65:
g2[i] = hexToByteArray[96](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] =
var kate: KzgCommitment
let res = blob_to_kzg_commitment(kate, blob, ctx.val)
verify(res)
ok(kate)
proc computeProof*(ctx: KzgCtx,
blobs: openArray[KzgBlob]):
Result[KzgProof, string] =
var proof: KzgProof
let res = compute_aggregate_kzg_proof(
proof,
blobs[0].getPtr,
blobs.len.csize_t,
ctx.val)
verify(res)
ok(proof)
proc verifyProof*(ctx: KzgCtx,
blobs: openArray[KzgBlob],
commitments: openArray[KzgCommitment],
proof: KzgProof): Result[void, string] =
if blobs.len == 0 or commitments.len == 0:
return err($KZG_BADARGS)
if blobs.len != commitments.len:
return err($KZG_BADARGS)
var ok: bool
let res = verify_aggregate_kzg_proof(
ok,
blobs[0].getPtr,
commitments[0].getPtr,
blobs.len.csize_t,
proof,
ctx.val)
verify(res)
if not ok:
return err($KZG_ERROR)
ok()
proc computeProof*(ctx: KzgCtx,
blob: KzgBlob,
z: Bytes32): Result[KzgProof, string] =
var proof: KzgProof
let res = compute_kzg_proof(
proof,
blob,
z,
ctx.val)
verify(res)
ok(proof)
proc verifyProof*(ctx: KzgCtx,
commitment: KzgCommitment,
z: Bytes32, # Input Point
y: Bytes32, # Claimed Value
proof: KzgProof): Result[void, string] =
var ok: bool
let res = verify_kzg_proof(
ok,
commitment,
z,
y,
proof,
ctx.val)
verify(res)
if not ok:
return err($KZG_ERROR)
ok()
{. pop .}

118
kzg4844/kzg_abi.nim Normal file
View File

@ -0,0 +1,118 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
std/strformat,
strutils
from os import DirSep
const FIELD_ELEMENTS_PER_BLOB*{.strdefine.} = 4096
const
ckzgPath = currentSourcePath.rsplit(DirSep, 1)[0] & "/csources/"
blstPath = ckzgPath & "blst/"
srcPath = ckzgPath & "src/"
bindingsPath = blstPath & "bindings"
when not defined(externalBlst):
{.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
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"
else: "kzg unknown error"
proc `==`*(a, b: KZG_RET): bool =
a.cint == b.cint
type
KzgBlob* = array[KzgBlobSize, byte]
KzgSettings* {.importc: "KZGSettings",
header: "c_kzg_4844.h", byref.} = object
Bytes48 = array[48, byte]
Bytes32 = array[32, byte]
KzgCommitment* = Bytes48
KzgProof* = Bytes48
{.pragma: kzg_abi, 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,
importc: "load_trusted_setup".}
proc load_trusted_setup_file*(res: KzgSettings,
input: File): KZG_RET {.kzg_abi,
importc: "load_trusted_setup_file" .}
proc free_trusted_setup*(s: KzgSettings) {.kzg_abi,
importc: "free_trusted_setup" .}
proc blob_to_kzg_commitment*(res: var KzgCommitment,
blob: KzgBlob,
s: KzgSettings): KZG_RET {.kzg_abi,
importc: "blob_to_kzg_commitment" .}
proc compute_aggregate_kzg_proof*(res: var KzgProof,
blobs: ptr KzgBlob,
n: csize_t,
s: KzgSettings): KZG_RET {.kzg_abi,
importc: "compute_aggregate_kzg_proof" .}
proc verify_aggregate_kzg_proof*(re: var bool,
blobs: ptr KzgBlob,
commitmentsBytes: ptr KzgCommitment,
n: csize_t,
aggregatedProofBytes: KzgProof,
s: KzgSettings): KZG_RET {.kzg_abi,
importc: "verify_aggregate_kzg_proof" .}
proc compute_kzg_proof*(res: var KzgProof,
blob: KzgBlob,
zBytes: Bytes32,
s: KzgSettings): KZG_RET {.kzg_abi,
importc: "compute_kzg_proof" .}
proc verify_kzg_proof*(res: var bool,
commitmentBytes: KzgCommitment,
zBytes: Bytes32,
yBytes: Bytes32,
proofBytes: KzgProof,
s: KzgSettings): KZG_RET {.kzg_abi,
importc: "verify_kzg_proof" .}

2
tests/config.nims Normal file
View File

@ -0,0 +1,2 @@
--styleCheck:usages
--styleCheck:error

118
tests/test_abi.nim Normal file
View File

@ -0,0 +1,118 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.used.}
import
std/[sysrand, streams, strutils],
unittest2,
stew/byteutils,
../kzg4844/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 agg proof success":
var kb = kzgs.createKateBlobs(nblobs)
var kp: KzgProof
var res = compute_aggregate_kzg_proof(kp, kb.blobs[0].addr,
csize_t(nblobs), kzgs)
check res == KZG_OK
var ok: bool
res = verify_aggregate_kzg_proof(ok, kb.blobs[0].addr,
kb.kates[0].addr, csize_t(nblobs), kp, kzgs)
check res == KZG_OK
check ok
test "verify agg proof failure":
var kb = kzgs.createKateBlobs(nblobs)
var kp: KzgProof
var res = compute_aggregate_kzg_proof(kp, kb.blobs[0].addr,
csize_t(nblobs), kzgs)
check res == KZG_OK
var other = kzgs.createKateBlobs(nblobs)
res = compute_aggregate_kzg_proof(kp, other.blobs[0].addr,
csize_t(nblobs), kzgs)
check res == KZG_OK
var ok: bool
res = verify_aggregate_kzg_proof(ok, kb.blobs[0].addr,
kb.kates[0].addr, csize_t(nblobs), kp, kzgs)
check res == KZG_OK
check ok == false
test "verify proof":
var kp: KzgProof
var res = compute_kzg_proof(kp, blob, inputPoint, kzgs)
check res == KZG_OK
check kp == proof
var ok: bool
res = verify_kzg_proof(ok, commitment, inputPoint, claimedValue, kp, kzgs)
check res == KZG_OK
check ok
free_trusted_setup(settings)

12
tests/test_all.nim Normal file
View File

@ -0,0 +1,12 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
test_abi,
test_kzg

69
tests/test_kzg.nim Normal file
View File

@ -0,0 +1,69 @@
# nim-kzg4844
# Copyright (c) 2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
{.used.}
import
std/[sysrand],
unittest2,
../kzg4844/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)":
const
trustedSetup = staticRead("../" & trustedSetupFile)
var ctx: KzgCtx
test "load trusted setup from string":
let res = loadTrustedSetupFromString(trustedSetup)
check res.isOk
ctx = res.get
test "verify proof success":
let kb = ctx.createKateBlobs(nblobs)
let pres = ctx.computeProof(kb.blobs)
check pres.isOk
let res = ctx.verifyProof(kb.blobs, kb.kates, pres.get)
check res.isOk
test "verify proof failure":
let kb = ctx.createKateBlobs(nblobs)
let pres = ctx.computeProof(kb.blobs)
check pres.isOk
let other = ctx.createKateBlobs(nblobs)
let badProof = ctx.computeProof(other.blobs)
check badProof.isOk
let res = ctx.verifyProof(kb.blobs, kb.kates, badProof.get)
check res.isErr
test "verify proof":
let kp = ctx.computeProof(blob, inputPoint)
check kp.isOk
check kp.get == proof
let res = ctx.verifyProof(commitment, inputPoint, claimedValue, kp.get)
check res.isOk

29
tests/types.nim Normal file

File diff suppressed because one or more lines are too long