You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在gnark v0.8.1中实现gRPC:Proof等类型转Go原生类型及序列化问题

Groth16 组件序列化/反序列化问题(gnark v0.8.1)

问题背景

在gnark v0.8.1环境下,需要将groth16.Proof、验证密钥(Verification Key)、公开见证(Public Witness)转换为Go原生[]byte类型。自行实现Serialize()和DeSerialize()函数时遇到错误。

核心问题

groth16.Proof是接口类型,其具体实现依赖于所选的椭圆曲线类型,直接使用gob进行序列化/反序列化时,无法统一处理不同曲线的Proof实例,且依赖内部类型会导致兼容性问题。

现有实现代码

import (
    "bytes"
    "encoding/gob"
    "errors"
    "fmt"

    "github.com/consensys/gnark/frontend"
    "github.com/consensys/gnark/frontend/witness"
    "github.com/consensys/gnark/std/accumulator/merkle/internal"
    "github.com/consensys/gnark/std/recursion/groth16"
    "github.com/consensys/gnark-crypto/ecc"
)

func Serialize(x any) ([]byte, error) {
    var buf bytes.Buffer
    enc := gob.NewEncoder(&buf)
    if err := enc.Encode(x); err != nil {
        return nil, err
    }
    fmt.Printf("buf.Bytes(): %v\n", buf.Bytes())
    return buf.Bytes(), nil
}

func DeSerialize(x []byte, returnType string) (interface{}, error) {
    fmt.Printf("x: %v\n", x)
    fmt.Printf("returnType: %v\n", returnType)
    buf := bytes.NewBuffer(x)
    dec := gob.NewDecoder(buf)
    switch returnType {
    case "proof":
        var proof groth16.Proof
        gob.Register(internal.Proof{})
        if err := dec.Decode(&proof); err != nil {
            fmt.Printf("----> Decode err: %v\n", err)
            return groth16.NewProof(ecc.BN254), err
        }
        return proof, nil

    case "publicWitness":
        var w witness.Witness
        if err := dec.Decode(&w); err != nil {
            res, _ := frontend.NewWitness(nil, ecc.BN254.ScalarField())
            return res, err
        }
        return w, nil

    case "verificationKey":
        var vk groth16.VerifyingKey
        if err := dec.Decode(&vk); err != nil {
            return vk, err
        }
        return vk, nil
    default:
        return nil, errors.New("wrong returnType")
    }
}

错误信息

运行代码时触发如下错误:

Decode err: gob: local interface type *groth16.Proof can only be decoded from remote interface type; received concrete type

Proof 结构定义(内部实现)

Proof = struct {
    Ar G1Affine = struct {
        X[4] uint;
        Y Element;
    };
    Krs G1Affine;
    Bs G2Affine = struct {
        X E2 = struct {
            A0 Element;
            A1 Element;
        };
        Y E2;
    };
    Commitment G1Affine;
    CommitmentPok G1Affine;
}

解决方案

gnark的核心组件(groth16.Proof、VerifyingKey、Witness等)都内置实现了BinaryMarshaler和BinaryUnmarshaler接口,无需使用gob序列化,直接调用内置方法即可完成类型与[]byte的转换,同时自动适配曲线类型差异。

修改后的序列化/反序列化函数

import (
    "errors"
    "fmt"

    "github.com/consensys/gnark/frontend"
    "github.com/consensys/gnark/frontend/witness"
    "github.com/consensys/gnark/std/recursion/groth16"
    "github.com/consensys/gnark-crypto/ecc"
)

// Serialize 将gnark组件转换为[]byte,依赖内置的MarshalBinary方法
func Serialize(x any) ([]byte, error) {
    marshaler, ok := x.(interface{ MarshalBinary() ([]byte, error) })
    if !ok {
        return nil, fmt.Errorf("type %T does not support binary serialization", x)
    }
    return marshaler.MarshalBinary()
}

// Deserialize 将[]byte转换为指定类型的gnark组件,需要传入曲线ID以创建对应实例
func Deserialize(data []byte, curve ecc.ID, returnType string) (interface{}, error) {
    switch returnType {
    case "proof":
        proof := groth16.NewProof(curve)
        if err := proof.UnmarshalBinary(data); err != nil {
            return nil, err
        }
        return proof, nil
    case "publicWitness":
        w, err := frontend.NewWitness(nil, curve.ScalarField())
        if err != nil {
            return nil, err
        }
        if err := w.UnmarshalBinary(data); err != nil {
            return nil, err
        }
        return w, nil
    case "verificationKey":
        vk := groth16.NewVerifyingKey(curve)
        if err := vk.UnmarshalBinary(data); err != nil {
            return nil, err
        }
        return vk, nil
    default:
        return nil, errors.New("invalid returnType: must be proof/publicWitness/verificationKey")
    }
}

方案说明

  1. 依赖内置方法:gnark为所有核心密码学组件实现了标准的二进制序列化接口,无需手动处理曲线相关的类型差异,避免依赖内部类型(如internal.Proof)导致的兼容性问题。
  2. 曲线ID参数:反序列化时需要传入曲线ID,确保创建的实例与序列化时的曲线类型一致,避免跨曲线的类型错误。
  3. 类型安全:通过类型断言确保输入类型支持序列化,比gob更贴合gnark的设计逻辑,减少序列化失败的概率。

内容的提问来源于stack exchange,提问作者Anupam Shaw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:43:15