Skip to content

Commit

Permalink
Ethereum EIP-4844: Go wrapper for KZG commitments (#318)
Browse files Browse the repository at this point in the history
* Go API: initial compilation

* cleanup KZG header

* go: add KZG prototypes

* KZG: Pass tests with Go API and -modfile=../go_test.mod

* Go KZG: Add Go to CI

* kzg go: fixes

* don't use direct linking on Windows, .lib files are rejected

* MacOS fscanf needs an extra char in the buffer

* go fmt

* typo
  • Loading branch information
mratsim authored Dec 13, 2023
1 parent 1f3e6b1 commit 0afccb4
Show file tree
Hide file tree
Showing 14 changed files with 770 additions and 278 deletions.
59 changes: 40 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
matrix:
nim_version: [version-1-6] # [version-1-4, devel]
rust_toolchain: [stable] # [beta, nightly]
go_toolchain: [stable]
target:
- os: linux
cpu: i386
Expand Down Expand Up @@ -78,21 +79,21 @@ jobs:
access_token: ${{ github.token }}

- name: Checkout constantine
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
path: constantine

- name: Restore MinGW-W64 (Windows) from cache
if: runner.os == 'Windows'
id: windows-mingw-cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: external/mingw-${{ matrix.target.cpu }}
key: 'mingw-${{ matrix.target.cpu }}'
- name: Restore Nim DLLs dependencies (Windows) from cache
if: runner.os == 'Windows'
id: windows-dlls-cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: external/dlls-${{ matrix.target.cpu }}
key: 'dlls-${{ matrix.target.cpu }}'
Expand Down Expand Up @@ -224,32 +225,23 @@ jobs:
update: false
install: base-devel git mingw-w64-x86_64-toolchain

- name: Install test dependencies (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
pacman -S --needed --noconfirm mingw-w64-x86_64-gmp mingw-w64-x86_64-llvm
nimble refresh --verbose -y
nimble install --verbose -y gmp jsony asynctools [email protected]
- name: Install test dependencies
if: runner.os != 'Windows'
shell: bash
run: |
nimble refresh --verbose -y
nimble install --verbose -y gmp jsony asynctools [email protected]
- name: Install Go [${{ matrix.go_toolchain }}]
uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go_toolchain }}

- name: Install Rust
- name: Install Rust [${{ matrix.rust_toolchain }}]
shell: bash
run: rustup update ${{ matrix.rust_toolchain }} && rustup default ${{ matrix.rust_toolchain }}

- name: Print Nim, Rust, LLVM versions and CPU specs.
- name: Print Nim, Go Rust, LLVM versions and CPU specs.
shell: bash
# gcc is an alias to Apple Clang on MacOS
run: |
nim -v
gcc -v
clang -v
go version
rustup --version
if [[ '${{ matrix.target.cpu }}' != 'i386' && '${{ runner.os }}' != 'Windows' ]]; then
llvm-config --version
Expand All @@ -263,6 +255,27 @@ jobs:
sysctl -a | grep hw.optional
fi
- name: Install test dependencies (Windows)
if: runner.os == 'Windows'
shell: msys2 {0}
run: |
pacman -S --needed --noconfirm mingw-w64-x86_64-gmp mingw-w64-x86_64-llvm
nimble refresh --verbose -y
nimble install --verbose -y gmp jsony asynctools [email protected]
cd constantine
go mod download -modfile=go_test.mod
- name: Install test dependencies
if: runner.os != 'Windows'
shell: bash
run: |
nimble refresh --verbose -y
nimble install --verbose -y gmp jsony asynctools [email protected]
cd constantine
go mod download -modfile=go_test.mod
- name: Run Constantine as C library tests (UNIX with Assembly)
if: runner.os != 'Windows' && matrix.target.BACKEND == 'ASM'
shell: bash
Expand Down Expand Up @@ -300,6 +313,14 @@ jobs:
nimble make_headers --verbose
nimble test_lib --verbose
- name: Run Constantine as Go library tests
# This reuses the static library built with `nimble make_lib`
if: matrix.target.cpu != 'i386'
shell: bash
run: |
cd constantine/constantine-go
go test -modfile=../go_test.mod
- name: Run Constantine as Rust library tests (with Assembly)
if: matrix.target.BACKEND == 'ASM' && matrix.target.cpu != 'i386'
shell: bash
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Executables shall be put in an ignored build/ directory
# Ignore dynamic, static libs and libtool archive files
# -----------------------------------------------------------------------------------------
build/
*.so
*.so.*
Expand All @@ -11,6 +12,10 @@ build/
*.exe
*.out

# Go
# -----------------------------------------------------------------------------------------
*.test

# Nim
# -----------------------------------------------------------------------------------------
nimcache/
Expand All @@ -37,6 +42,10 @@ Cargo.lock
# -----------------------------------------------------------------------------------------
*.sage.py

# MacOS
# -----------------------------------------------------------------------------------------
*.dylib.dSYM/

# Swap or debug
# -----------------------------------------------------------------------------------------
*.swp
Expand Down
1 change: 1 addition & 0 deletions bindings/lib_constantine.nim
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import
../constantine/csprngs,
# Protocols
../constantine/ethereum_bls_signatures,
../constantine/trusted_setups/ethereum_kzg_srs,
../constantine/ethereum_eip4844_kzg,

# Ensure globals like proc from kernel32.dll are populated at library load time
Expand Down
180 changes: 180 additions & 0 deletions constantine-go/constantine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
/** 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.
*/

package constantine

/*
#cgo CFLAGS: -I"${SRCDIR}/../include"
#cgo !windows LDFLAGS: "${SRCDIR}/../lib/libconstantine.a"
// The ending in .lib is rejected, so we can't use the direct linking syntax:
// https://github.com/golang/go/blob/46ea4ab/src/cmd/go/internal/work/security.go#L216
// #cgo windows LDFLAGS: "${SRCDIR}/../lib/constantine.lib"
#cgo windows LDFLAGS: -L"${SRCDIR}/../lib" -Wl,-Bstatic -lconstantine -Wl,-Bdynamic
#include <stdlib.h>
#include <constantine.h>
*/
import "C"
import (
"errors"
"unsafe"
)

// Threadpool API
// -----------------------------------------------------

type Threadpool struct {
ctx *C.ctt_threadpool
}

func ThreadpoolNew(numThreads int) Threadpool {
return Threadpool{
ctx: C.ctt_threadpool_new(C.size_t(numThreads)),
}
}

func (tp Threadpool) Shutdown() {
C.ctt_threadpool_shutdown(tp.ctx)
}

// Ethereum EIP-4844 KZG API
// -----------------------------------------------------

type (
EthKzgCommitment [48]byte
EthKzgProof [48]byte
EthBlob [4096 * 32]byte
EthKzgChallenge [32]byte
EthKzgEvalAtChallenge [32]byte
)

type EthKzgContext struct {
cCtx *C.ctt_eth_kzg_context
}

func EthKzgContextNew(trustedSetupFile string) (ctx EthKzgContext, err error) {
cFile := C.CString(trustedSetupFile)
defer C.free(unsafe.Pointer(cFile))
status := C.ctt_eth_trusted_setup_load(
&ctx.cCtx,
cFile,
C.cttEthTSFormat_ckzg4844,
)
if status != C.cttEthTS_Success {
err = errors.New(
C.GoString(C.ctt_eth_trusted_setup_status_to_string(status)),
)
}
return ctx, err
}

func (ctx EthKzgContext) Delete() {
C.ctt_eth_trusted_setup_delete(ctx.cCtx)
}

func (ctx EthKzgContext) BlobToKZGCommitment(blob EthBlob) (commitment EthKzgCommitment, err error) {
status := C.ctt_eth_kzg_blob_to_kzg_commitment(
ctx.cCtx,
(*C.ctt_eth_kzg_commitment)(unsafe.Pointer(&commitment)),
(*C.ctt_eth_kzg_blob)(unsafe.Pointer(&blob)),
)
if status != C.cttEthKzg_Success {
err = errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
}
return commitment, err
}

func (ctx EthKzgContext) ComputeKzgProof(blob EthBlob, z EthKzgChallenge) (proof EthKzgProof, y EthKzgEvalAtChallenge, err error) {
status := C.ctt_eth_kzg_compute_kzg_proof(
ctx.cCtx,
(*C.ctt_eth_kzg_proof)(unsafe.Pointer(&proof)),
(*C.ctt_eth_kzg_eval_at_challenge)(unsafe.Pointer(&y)),
(*C.ctt_eth_kzg_blob)(unsafe.Pointer(&blob)),
(*C.ctt_eth_kzg_challenge)(unsafe.Pointer(&z)),
)
if status != C.cttEthKzg_Success {
err = errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
}
return proof, y, err
}

func (ctx EthKzgContext) VerifyKzgProof(commitment EthKzgCommitment, z EthKzgChallenge, y EthKzgEvalAtChallenge, proof EthKzgProof) (bool, error) {
status := C.ctt_eth_kzg_verify_kzg_proof(
ctx.cCtx,
(*C.ctt_eth_kzg_commitment)(unsafe.Pointer(&commitment)),
(*C.ctt_eth_kzg_challenge)(unsafe.Pointer(&z)),
(*C.ctt_eth_kzg_eval_at_challenge)(unsafe.Pointer(&y)),
(*C.ctt_eth_kzg_proof)(unsafe.Pointer(&proof)),
)
if status != C.cttEthKzg_Success {
err := errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
return false, err
}
return true, nil
}

func (ctx EthKzgContext) ComputeBlobKzgProof(blob EthBlob, commitment EthKzgCommitment) (proof EthKzgProof, err error) {
status := C.ctt_eth_kzg_compute_blob_kzg_proof(
ctx.cCtx,
(*C.ctt_eth_kzg_proof)(unsafe.Pointer(&proof)),
(*C.ctt_eth_kzg_blob)(unsafe.Pointer(&blob)),
(*C.ctt_eth_kzg_commitment)(unsafe.Pointer(&commitment)),
)
if status != C.cttEthKzg_Success {
err = errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
}
return proof, err
}

func (ctx EthKzgContext) VerifyBlobKzgProof(blob EthBlob, commitment EthKzgCommitment, proof EthKzgProof) (bool, error) {
status := C.ctt_eth_kzg_verify_blob_kzg_proof(
ctx.cCtx,
(*C.ctt_eth_kzg_blob)(unsafe.Pointer(&blob)),
(*C.ctt_eth_kzg_commitment)(unsafe.Pointer(&commitment)),
(*C.ctt_eth_kzg_proof)(unsafe.Pointer(&proof)),
)
if status != C.cttEthKzg_Success {
err := errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
return false, err
}
return true, nil
}

func (ctx EthKzgContext) VerifyBlobKzgProofBatch(blobs []EthBlob, commitments []EthKzgCommitment, proofs []EthKzgProof, secureRandomBytes [32]byte) (bool, error) {

if len(blobs) != len(commitments) || len(blobs) != len(proofs) {
return false, errors.New("VerifyBlobKzgProofBatch: Lengths of inputs do not match.")
}

status := C.ctt_eth_kzg_verify_blob_kzg_proof_batch(
ctx.cCtx,
*(**C.ctt_eth_kzg_blob)(unsafe.Pointer(&blobs)),
*(**C.ctt_eth_kzg_commitment)(unsafe.Pointer(&commitments)),
*(**C.ctt_eth_kzg_proof)(unsafe.Pointer(&proofs)),
(C.size_t)(len(blobs)),
(*C.uint8_t)(unsafe.Pointer(&secureRandomBytes)),
)
if status != C.cttEthKzg_Success {
err := errors.New(
C.GoString(C.ctt_eth_kzg_status_to_string(status)),
)
return false, err
}
return true, nil
}
Loading

0 comments on commit 0afccb4

Please sign in to comment.