Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 92 additions & 49 deletions bindings/go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ package ckzg4844
import "C"

import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"strings"
"unsafe"

// So its functions are available during compilation.
Expand Down Expand Up @@ -38,78 +38,79 @@ var (
ErrBadArgs = errors.New("bad arguments")
ErrError = errors.New("unexpected error")
ErrMalloc = errors.New("malloc failed")
errorMap = map[C.C_KZG_RET]error{
C.C_KZG_OK: nil,
C.C_KZG_BADARGS: ErrBadArgs,
C.C_KZG_ERROR: ErrError,
C.C_KZG_MALLOC: ErrMalloc,
}
)

///////////////////////////////////////////////////////////////////////////////
// Helper Functions
///////////////////////////////////////////////////////////////////////////////

// makeErrorFromRet translates an (integral) return value, as reported
// by the C library, into a proper Go error. If there is no error, this
// will return nil.
// by the C library, into a proper Go error. This function should only be
// called when there is an error, not with C_KZG_OK.
func makeErrorFromRet(ret C.C_KZG_RET) error {
err, ok := errorMap[ret]
if !ok {
panic(fmt.Sprintf("unexpected return value: %v", ret))
switch ret {
case C.C_KZG_BADARGS:
return ErrBadArgs
case C.C_KZG_ERROR:
return ErrError
case C.C_KZG_MALLOC:
return ErrMalloc
}
return err
return fmt.Errorf("unexpected error from c-library: %v", ret)
}

///////////////////////////////////////////////////////////////////////////////
// Unmarshal Functions
///////////////////////////////////////////////////////////////////////////////

func (b *Bytes32) UnmarshalText(input []byte) error {
inputStr := string(input)
if strings.HasPrefix(inputStr, "0x") {
inputStr = strings.TrimPrefix(inputStr, "0x")
if bytes.HasPrefix(input, []byte("0x")) {
input = input[2:]
}
if len(input) != 2*len(b) {
return ErrBadArgs
}
bytes, err := hex.DecodeString(inputStr)
l, err := hex.Decode(b[:], input)
if err != nil {
return err
}
if len(bytes) != len(b) {
if l != len(b) {
return ErrBadArgs
}
copy(b[:], bytes)
return nil
}

func (b *Bytes48) UnmarshalText(input []byte) error {
inputStr := string(input)
if strings.HasPrefix(inputStr, "0x") {
inputStr = strings.TrimPrefix(inputStr, "0x")
if bytes.HasPrefix(input, []byte("0x")) {
input = input[2:]
}
bytes, err := hex.DecodeString(inputStr)
if len(input) != 2*len(b) {
return ErrBadArgs
}
l, err := hex.Decode(b[:], input)
if err != nil {
return err
}
if len(bytes) != len(b) {
if l != len(b) {
return ErrBadArgs
}
copy(b[:], bytes)
return nil
}

func (b *Blob) UnmarshalText(input []byte) error {
inputStr := string(input)
if strings.HasPrefix(inputStr, "0x") {
inputStr = strings.TrimPrefix(inputStr, "0x")
if bytes.HasPrefix(input, []byte("0x")) {
input = input[2:]
}
if len(input) != 2*len(b) {
return ErrBadArgs
}
bytes, err := hex.DecodeString(inputStr)
l, err := hex.Decode(b[:], input)
if err != nil {
return err
}
if len(bytes) != len(b) {
if l != len(b) {
return ErrBadArgs
}
copy(b[:], bytes)
return nil
}

Expand Down Expand Up @@ -147,6 +148,7 @@ func LoadTrustedSetup(g1Bytes, g2Bytes []byte) error {
(C.size_t)(numG2Elements))
if ret == C.C_KZG_OK {
loaded = true
return nil
}
return makeErrorFromRet(ret)
}
Expand Down Expand Up @@ -174,6 +176,7 @@ func LoadTrustedSetupFile(trustedSetupFile string) error {
C.fclose(fp)
if ret == C.C_KZG_OK {
loaded = true
return nil
}
return makeErrorFromRet(ret)
}
Expand All @@ -200,16 +203,24 @@ BlobToKZGCommitment is the binding for:
const Blob *blob,
const KZGSettings *s);
*/
func BlobToKZGCommitment(blob Blob) (KZGCommitment, error) {
func BlobToKZGCommitment(blob *Blob) (KZGCommitment, error) {
if !loaded {
panic("trusted setup isn't loaded")
}
commitment := KZGCommitment{}
if blob == nil {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so defensive? If caller sends in nil, he should not be surprised if a nil deref happens, imo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather be safe than sorry. It's a very simple check, might as well.

return KZGCommitment{}, ErrBadArgs
}

var commitment KZGCommitment
ret := C.blob_to_kzg_commitment(
(*C.KZGCommitment)(unsafe.Pointer(&commitment)),
(*C.Blob)(unsafe.Pointer(&blob)),
(*C.Blob)(unsafe.Pointer(blob)),
&settings)
return commitment, makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return KZGCommitment{}, makeErrorFromRet(ret)
}
return commitment, nil
}

/*
Expand All @@ -222,19 +233,28 @@ ComputeKZGProof is the binding for:
const Bytes32 *z_bytes,
const KZGSettings *s);
*/
func ComputeKZGProof(blob Blob, zBytes Bytes32) (KZGProof, Bytes32, error) {
func ComputeKZGProof(blob *Blob, zBytes Bytes32) (KZGProof, Bytes32, error) {
if !loaded {
panic("trusted setup isn't loaded")
}
proof := KZGProof{}
y := Bytes32{}
if blob == nil {
return KZGProof{}, Bytes32{}, ErrBadArgs
}
var (
proof KZGProof
y Bytes32
)
ret := C.compute_kzg_proof(
(*C.KZGProof)(unsafe.Pointer(&proof)),
(*C.Bytes32)(unsafe.Pointer(&y)),
(*C.Blob)(unsafe.Pointer(&blob)),
(*C.Blob)(unsafe.Pointer(blob)),
(*C.Bytes32)(unsafe.Pointer(&zBytes)),
&settings)
return proof, y, makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return KZGProof{}, Bytes32{}, makeErrorFromRet(ret)
}
return proof, y, nil
}

/*
Expand All @@ -246,17 +266,24 @@ ComputeBlobKZGProof is the binding for:
const Bytes48 *commitment_bytes,
const KZGSettings *s);
*/
func ComputeBlobKZGProof(blob Blob, commitmentBytes Bytes48) (KZGProof, error) {
func ComputeBlobKZGProof(blob *Blob, commitmentBytes Bytes48) (KZGProof, error) {
if !loaded {
panic("trusted setup isn't loaded")
}
proof := KZGProof{}
if blob == nil {
return KZGProof{}, ErrBadArgs
}
var proof KZGProof
ret := C.compute_blob_kzg_proof(
(*C.KZGProof)(unsafe.Pointer(&proof)),
(*C.Blob)(unsafe.Pointer(&blob)),
(*C.Blob)(unsafe.Pointer(blob)),
(*C.Bytes48)(unsafe.Pointer(&commitmentBytes)),
&settings)
return proof, makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return KZGProof{}, makeErrorFromRet(ret)
}
return proof, nil
}

/*
Expand All @@ -282,7 +309,11 @@ func VerifyKZGProof(commitmentBytes Bytes48, zBytes, yBytes Bytes32, proofBytes
(*C.Bytes32)(unsafe.Pointer(&yBytes)),
(*C.Bytes48)(unsafe.Pointer(&proofBytes)),
&settings)
return bool(result), makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return false, makeErrorFromRet(ret)
}
return bool(result), nil
}

/*
Expand All @@ -295,18 +326,26 @@ VerifyBlobKZGProof is the binding for:
const Bytes48 *proof_bytes,
const KZGSettings *s);
*/
func VerifyBlobKZGProof(blob Blob, commitmentBytes, proofBytes Bytes48) (bool, error) {
func VerifyBlobKZGProof(blob *Blob, commitmentBytes, proofBytes Bytes48) (bool, error) {
if !loaded {
panic("trusted setup isn't loaded")
}
if blob == nil {
return false, ErrBadArgs
}

var result C.bool
ret := C.verify_blob_kzg_proof(
&result,
(*C.Blob)(unsafe.Pointer(&blob)),
(*C.Blob)(unsafe.Pointer(blob)),
(*C.Bytes48)(unsafe.Pointer(&commitmentBytes)),
(*C.Bytes48)(unsafe.Pointer(&proofBytes)),
&settings)
return bool(result), makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return false, makeErrorFromRet(ret)
}
return bool(result), nil
}

/*
Expand Down Expand Up @@ -335,5 +374,9 @@ func VerifyBlobKZGProofBatch(blobs []Blob, commitmentsBytes, proofsBytes []Bytes
*(**C.Bytes48)(unsafe.Pointer(&proofsBytes)),
(C.size_t)(len(blobs)),
&settings)
return bool(result), makeErrorFromRet(ret)

if ret != C.C_KZG_OK {
return false, makeErrorFromRet(ret)
}
return bool(result), nil
}
33 changes: 16 additions & 17 deletions bindings/go/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (
)

func TestMain(m *testing.M) {
err := LoadTrustedSetupFile("../../src/trusted_setup.txt")
if err != nil {
panic("failed to load trusted setup")

if err := LoadTrustedSetupFile("../../src/trusted_setup.txt"); err != nil {
panic(fmt.Sprintf("failed to load trusted setup: %v", err))
}
defer FreeTrustedSetup()
code := m.Run()
Expand All @@ -40,13 +40,11 @@ func getRandFieldElement(seed int64) Bytes32 {
return fieldElementBytes
}

func getRandBlob(seed int64) Blob {
var blob Blob
func fillBlobRandom(blob *Blob, seed int64) {
for i := 0; i < BytesPerBlob; i += BytesPerFieldElement {
fieldElementBytes := getRandFieldElement(seed + int64(i))
copy(blob[i:i+BytesPerFieldElement], fieldElementBytes[:])
}
return blob
}

///////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -84,7 +82,7 @@ func TestBlobToKZGCommitment(t *testing.T) {
require.NoError(t, testFile.Close())
require.NoError(t, err)

var blob Blob
blob := new(Blob)
err = blob.UnmarshalText([]byte(test.Input.Blob))
if err != nil {
require.Nil(t, test.Output)
Expand Down Expand Up @@ -124,7 +122,7 @@ func TestComputeKZGProof(t *testing.T) {
require.NoError(t, testFile.Close())
require.NoError(t, err)

var blob Blob
blob := new(Blob)
err = blob.UnmarshalText([]byte(test.Input.Blob))
if err != nil {
require.Nil(t, test.Output)
Expand Down Expand Up @@ -178,7 +176,7 @@ func TestComputeBlobKZGProof(t *testing.T) {
require.NoError(t, testFile.Close())
require.NoError(t, err)

var blob Blob
blob := new(Blob)
err = blob.UnmarshalText([]byte(test.Input.Blob))
if err != nil {
require.Nil(t, test.Output)
Expand Down Expand Up @@ -289,7 +287,7 @@ func TestVerifyBlobKZGProof(t *testing.T) {
require.NoError(t, testFile.Close())
require.NoError(t, err)

var blob Blob
var blob = new(Blob)
err = blob.UnmarshalText([]byte(test.Input.Blob))
if err != nil {
require.Nil(t, test.Output)
Expand Down Expand Up @@ -399,10 +397,11 @@ func Benchmark(b *testing.B) {
proofs := [length]Bytes48{}
fields := [length]Bytes32{}
for i := 0; i < length; i++ {
blob := getRandBlob(int64(i))
commitment, err := BlobToKZGCommitment(blob)
var blob Blob
fillBlobRandom(&blob, int64(i))
commitment, err := BlobToKZGCommitment(&blob)
require.NoError(b, err)
proof, err := ComputeBlobKZGProof(blob, Bytes48(commitment))
proof, err := ComputeBlobKZGProof(&blob, Bytes48(commitment))
require.NoError(b, err)

blobs[i] = blob
Expand All @@ -413,19 +412,19 @@ func Benchmark(b *testing.B) {

b.Run("BlobToKZGCommitment", func(b *testing.B) {
for n := 0; n < b.N; n++ {
BlobToKZGCommitment(blobs[0])
BlobToKZGCommitment(&blobs[0])
}
})

b.Run("ComputeKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
ComputeKZGProof(blobs[0], fields[0])
ComputeKZGProof(&blobs[0], fields[0])
}
})

b.Run("ComputeBlobKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
ComputeBlobKZGProof(blobs[0], commitments[0])
ComputeBlobKZGProof(&blobs[0], commitments[0])
}
})

Expand All @@ -437,7 +436,7 @@ func Benchmark(b *testing.B) {

b.Run("VerifyBlobKZGProof", func(b *testing.B) {
for n := 0; n < b.N; n++ {
VerifyBlobKZGProof(blobs[0], commitments[0], proofs[0])
VerifyBlobKZGProof(&blobs[0], commitments[0], proofs[0])
}
})

Expand Down