package parsers
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
bn256 "github.com/ethereum/go-ethereum/crypto/bn256/cloudflare"
|
|
"github.com/iden3/go-circom-prover-verifier/types"
|
|
)
|
|
|
|
// PkString is the equivalent to the Pk struct in string representation, containing the ProvingKey
|
|
type PkString struct {
|
|
A [][]string `json:"A"`
|
|
B2 [][][]string `json:"B2"`
|
|
B1 [][]string `json:"B1"`
|
|
C [][]string `json:"C"`
|
|
NVars int `json:"nVars"`
|
|
NPublic int `json:"nPublic"`
|
|
VkAlpha1 []string `json:"vk_alfa_1"`
|
|
VkDelta1 []string `json:"vk_delta_1"`
|
|
VkBeta1 []string `json:"vk_beta_1"`
|
|
VkBeta2 [][]string `json:"vk_beta_2"`
|
|
VkDelta2 [][]string `json:"vk_delta_2"`
|
|
HExps [][]string `json:"hExps"`
|
|
DomainSize int `json:"domainSize"`
|
|
PolsA []map[string]string `json:"polsA"`
|
|
PolsB []map[string]string `json:"polsB"`
|
|
}
|
|
|
|
// WitnessString contains the Witness in string representation
|
|
type WitnessString []string
|
|
|
|
// ProofString is the equivalent to the Proof struct in string representation
|
|
type ProofString struct {
|
|
A []string `json:"pi_a"`
|
|
B [][]string `json:"pi_b"`
|
|
C []string `json:"pi_c"`
|
|
Protocol string `json:"protocol"`
|
|
}
|
|
|
|
// VkString is the Verification Key data structure in string format (from json)
|
|
type VkString struct {
|
|
Alpha []string `json:"vk_alfa_1"`
|
|
Beta [][]string `json:"vk_beta_2"`
|
|
Gamma [][]string `json:"vk_gamma_2"`
|
|
Delta [][]string `json:"vk_delta_2"`
|
|
IC [][]string `json:"IC"`
|
|
}
|
|
|
|
// ParseWitness parses the json []byte data into the Witness struct
|
|
func ParseWitness(wJson []byte) (types.Witness, error) {
|
|
var ws WitnessString
|
|
err := json.Unmarshal(wJson, &ws)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var w types.Witness
|
|
for i := 0; i < len(ws); i++ {
|
|
bi, err := stringToBigInt(ws[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
w = append(w, bi)
|
|
}
|
|
return w, nil
|
|
}
|
|
|
|
// ParsePk parses the json []byte data into the Pk struct
|
|
func ParsePk(pkJson []byte) (*types.Pk, error) {
|
|
var pkStr PkString
|
|
err := json.Unmarshal(pkJson, &pkStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk, err := pkStringToPk(pkStr)
|
|
return pk, err
|
|
}
|
|
|
|
func pkStringToPk(ps PkString) (*types.Pk, error) {
|
|
var p types.Pk
|
|
var err error
|
|
|
|
p.A, err = arrayStringToG1(ps.A)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.B2, err = arrayStringToG2(ps.B2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.B1, err = arrayStringToG1(ps.B1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.C, err = arrayStringToG1(ps.C)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.NVars = ps.NVars
|
|
p.NPublic = ps.NPublic
|
|
|
|
p.VkAlpha1, err = stringToG1(ps.VkAlpha1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.VkDelta1, err = stringToG1(ps.VkDelta1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.VkBeta1, err = stringToG1(ps.VkBeta1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.VkBeta2, err = stringToG2(ps.VkBeta2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.VkDelta2, err = stringToG2(ps.VkDelta2)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.HExps, err = arrayStringToG1(ps.HExps)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.DomainSize = ps.DomainSize
|
|
|
|
p.PolsA, err = polsStringToBigInt(ps.PolsA)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p.PolsB, err = polsStringToBigInt(ps.PolsB)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &p, nil
|
|
}
|
|
|
|
func proofStringToProof(pr ProofString) (*types.Proof, error) {
|
|
var p types.Proof
|
|
var err error
|
|
p.A, err = stringToG1(pr.A)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.B, err = stringToG2(pr.B)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
p.C, err = stringToG1(pr.C)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &p, nil
|
|
}
|
|
|
|
// ParseProof takes a json []byte and outputs the *Proof struct
|
|
func ParseProof(pj []byte) (*types.Proof, error) {
|
|
var pr ProofString
|
|
err := json.Unmarshal(pj, &pr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p, err := proofStringToProof(pr)
|
|
return p, err
|
|
}
|
|
|
|
// ParsePublicSignals takes a json []byte and outputs the []*big.Int struct
|
|
func ParsePublicSignals(pj []byte) ([]*big.Int, error) {
|
|
var pr []string
|
|
err := json.Unmarshal(pj, &pr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var public []*big.Int
|
|
for _, s := range pr {
|
|
sb, err := stringToBigInt(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
public = append(public, sb)
|
|
}
|
|
return public, nil
|
|
}
|
|
|
|
// ParseVk takes a json []byte and outputs the *Vk struct
|
|
func ParseVk(vj []byte) (*types.Vk, error) {
|
|
var vr VkString
|
|
err := json.Unmarshal(vj, &vr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v, err := vkStringToVk(vr)
|
|
return v, err
|
|
}
|
|
|
|
func vkStringToVk(vr VkString) (*types.Vk, error) {
|
|
var v types.Vk
|
|
var err error
|
|
v.Alpha, err = stringToG1(vr.Alpha)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v.Beta, err = stringToG2(vr.Beta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v.Gamma, err = stringToG2(vr.Gamma)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
v.Delta, err = stringToG2(vr.Delta)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := 0; i < len(vr.IC); i++ {
|
|
p, err := stringToG1(vr.IC[i])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
v.IC = append(v.IC, p)
|
|
}
|
|
|
|
return &v, nil
|
|
}
|
|
|
|
// polsStringToBigInt is for taking string polynomials and converting it to *big.Int polynomials
|
|
func polsStringToBigInt(s []map[string]string) ([]map[int]*big.Int, error) {
|
|
var o []map[int]*big.Int
|
|
for i := 0; i < len(s); i++ {
|
|
// var oi map[int]*big.Int
|
|
oi := make(map[int]*big.Int)
|
|
for j, v := range s[i] {
|
|
si, err := stringToBigInt(v)
|
|
if err != nil {
|
|
return o, err
|
|
}
|
|
// oi = append(oi, si)
|
|
jInt, err := strconv.Atoi(j)
|
|
if err != nil {
|
|
return o, err
|
|
}
|
|
oi[jInt] = si
|
|
}
|
|
o = append(o, oi)
|
|
}
|
|
return o, nil
|
|
}
|
|
|
|
// ArrayBigIntToString converts an []*big.Int into []string, used to output the Public Signals
|
|
func ArrayBigIntToString(bi []*big.Int) []string {
|
|
var s []string
|
|
for i := 0; i < len(bi); i++ {
|
|
s = append(s, bi[i].String())
|
|
}
|
|
return s
|
|
}
|
|
|
|
func arrayStringToBigInt(s []string) ([]*big.Int, error) {
|
|
var o []*big.Int
|
|
for i := 0; i < len(s); i++ {
|
|
si, err := stringToBigInt(s[i])
|
|
if err != nil {
|
|
return o, nil
|
|
}
|
|
o = append(o, si)
|
|
}
|
|
return o, nil
|
|
}
|
|
|
|
func stringToBigInt(s string) (*big.Int, error) {
|
|
base := 10
|
|
if bytes.HasPrefix([]byte(s), []byte("0x")) {
|
|
base = 16
|
|
s = strings.TrimPrefix(s, "0x")
|
|
}
|
|
n, ok := new(big.Int).SetString(s, base)
|
|
if !ok {
|
|
return nil, fmt.Errorf("Can not parse string to *big.Int: %s", s)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func addPadding32(b []byte) []byte {
|
|
if len(b) != 32 {
|
|
b = addZPadding(b)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func addZPadding(b []byte) []byte {
|
|
var z [32]byte
|
|
var r []byte
|
|
r = append(r, z[len(b):]...) // add padding on the left
|
|
r = append(r, b...)
|
|
return r[:32]
|
|
}
|
|
|
|
func stringToBytes(s string) ([]byte, error) {
|
|
if s == "1" {
|
|
s = "0"
|
|
}
|
|
bi, ok := new(big.Int).SetString(s, 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing bigint stringToBytes")
|
|
}
|
|
b := bi.Bytes()
|
|
if len(b) != 32 {
|
|
b = addZPadding(b)
|
|
}
|
|
return b, nil
|
|
|
|
}
|
|
|
|
func arrayStringToG1(h [][]string) ([]*bn256.G1, error) {
|
|
var o []*bn256.G1
|
|
for i := 0; i < len(h); i++ {
|
|
hi, err := stringToG1(h[i])
|
|
if err != nil {
|
|
return o, err
|
|
}
|
|
o = append(o, hi)
|
|
}
|
|
return o, nil
|
|
}
|
|
|
|
func arrayStringToG2(h [][][]string) ([]*bn256.G2, error) {
|
|
var o []*bn256.G2
|
|
for i := 0; i < len(h); i++ {
|
|
hi, err := stringToG2(h[i])
|
|
if err != nil {
|
|
return o, err
|
|
}
|
|
o = append(o, hi)
|
|
}
|
|
return o, nil
|
|
}
|
|
|
|
func stringToG1(h []string) (*bn256.G1, error) {
|
|
if len(h) <= 2 {
|
|
return nil, fmt.Errorf("not enought data for stringToG1")
|
|
}
|
|
h = h[:2]
|
|
hexa := false
|
|
if len(h[0]) > 1 {
|
|
if "0x" == h[0][:2] {
|
|
hexa = true
|
|
}
|
|
}
|
|
in := ""
|
|
|
|
var b []byte
|
|
var err error
|
|
if hexa {
|
|
for i := range h {
|
|
in += strings.TrimPrefix(h[i], "0x")
|
|
}
|
|
b, err = hex.DecodeString(in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
// TODO TMP
|
|
// TODO use stringToBytes()
|
|
if h[0] == "1" {
|
|
h[0] = "0"
|
|
}
|
|
if h[1] == "1" {
|
|
h[1] = "0"
|
|
}
|
|
bi0, ok := new(big.Int).SetString(h[0], 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing stringToG1")
|
|
}
|
|
bi1, ok := new(big.Int).SetString(h[1], 10)
|
|
if !ok {
|
|
return nil, fmt.Errorf("error parsing stringToG1")
|
|
}
|
|
b0 := bi0.Bytes()
|
|
b1 := bi1.Bytes()
|
|
if len(b0) != 32 {
|
|
b0 = addZPadding(b0)
|
|
}
|
|
if len(b1) != 32 {
|
|
b1 = addZPadding(b1)
|
|
}
|
|
|
|
b = append(b, b0...)
|
|
b = append(b, b1...)
|
|
}
|
|
p := new(bn256.G1)
|
|
_, err = p.Unmarshal(b)
|
|
|
|
return p, err
|
|
}
|
|
|
|
func stringToG2(h [][]string) (*bn256.G2, error) {
|
|
if len(h) <= 2 {
|
|
return nil, fmt.Errorf("not enought data for stringToG2")
|
|
}
|
|
h = h[:2]
|
|
hexa := false
|
|
if len(h[0][0]) > 1 {
|
|
if "0x" == h[0][0][:2] {
|
|
hexa = true
|
|
}
|
|
}
|
|
in := ""
|
|
var b []byte
|
|
var err error
|
|
if hexa {
|
|
for i := 0; i < len(h); i++ {
|
|
for j := 0; j < len(h[i]); j++ {
|
|
in += strings.TrimPrefix(h[i][j], "0x")
|
|
}
|
|
}
|
|
b, err = hex.DecodeString(in)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
// TODO TMP
|
|
bH, err := stringToBytes(h[0][1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, bH...)
|
|
bH, err = stringToBytes(h[0][0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, bH...)
|
|
bH, err = stringToBytes(h[1][1])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, bH...)
|
|
bH, err = stringToBytes(h[1][0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b = append(b, bH...)
|
|
}
|
|
|
|
p := new(bn256.G2)
|
|
_, err = p.Unmarshal(b)
|
|
return p, err
|
|
}
|
|
|
|
// ProofStringToSmartContractFormat converts the ProofString to a ProofString in the SmartContract format in a ProofString structure
|
|
func ProofStringToSmartContractFormat(s ProofString) ProofString {
|
|
var rs ProofString
|
|
rs.A = make([]string, 2)
|
|
rs.B = make([][]string, 2)
|
|
rs.B[0] = make([]string, 2)
|
|
rs.B[1] = make([]string, 2)
|
|
rs.C = make([]string, 2)
|
|
|
|
rs.A[0] = s.A[0]
|
|
rs.A[1] = s.A[1]
|
|
rs.B[0][0] = s.B[0][1]
|
|
rs.B[0][1] = s.B[0][0]
|
|
rs.B[1][0] = s.B[1][1]
|
|
rs.B[1][1] = s.B[1][0]
|
|
rs.C[0] = s.C[0]
|
|
rs.C[1] = s.C[1]
|
|
rs.Protocol = s.Protocol
|
|
return rs
|
|
}
|
|
|
|
// ProofToSmartContractFormat converts the *types.Proof to a ProofString in the SmartContract format in a ProofString structure
|
|
func ProofToSmartContractFormat(p *types.Proof) ProofString {
|
|
s := ProofToString(p)
|
|
return ProofStringToSmartContractFormat(s)
|
|
}
|
|
|
|
// ProofToString converts the Proof to ProofString
|
|
func ProofToString(p *types.Proof) ProofString {
|
|
var ps ProofString
|
|
ps.A = make([]string, 3)
|
|
ps.B = make([][]string, 3)
|
|
ps.B[0] = make([]string, 2)
|
|
ps.B[1] = make([]string, 2)
|
|
ps.B[2] = make([]string, 2)
|
|
ps.C = make([]string, 3)
|
|
|
|
a := p.A.Marshal()
|
|
ps.A[0] = new(big.Int).SetBytes(a[:32]).String()
|
|
ps.A[1] = new(big.Int).SetBytes(a[32:64]).String()
|
|
ps.A[2] = "1"
|
|
|
|
b := p.B.Marshal()
|
|
ps.B[0][1] = new(big.Int).SetBytes(b[:32]).String()
|
|
ps.B[0][0] = new(big.Int).SetBytes(b[32:64]).String()
|
|
ps.B[1][1] = new(big.Int).SetBytes(b[64:96]).String()
|
|
ps.B[1][0] = new(big.Int).SetBytes(b[96:128]).String()
|
|
ps.B[2][0] = "1"
|
|
ps.B[2][1] = "0"
|
|
|
|
c := p.C.Marshal()
|
|
ps.C[0] = new(big.Int).SetBytes(c[:32]).String()
|
|
ps.C[1] = new(big.Int).SetBytes(c[32:64]).String()
|
|
ps.C[2] = "1"
|
|
|
|
ps.Protocol = "groth"
|
|
|
|
return ps
|
|
}
|
|
|
|
// ProofToJson outputs the Proof i Json format
|
|
func ProofToJson(p *types.Proof) ([]byte, error) {
|
|
ps := ProofToString(p)
|
|
return json.Marshal(ps)
|
|
}
|
|
|
|
// ProofToHex converts the Proof to ProofString with hexadecimal strings
|
|
func ProofToHex(p *types.Proof) ProofString {
|
|
var ps ProofString
|
|
ps.A = make([]string, 3)
|
|
ps.B = make([][]string, 3)
|
|
ps.B[0] = make([]string, 2)
|
|
ps.B[1] = make([]string, 2)
|
|
ps.B[2] = make([]string, 2)
|
|
ps.C = make([]string, 3)
|
|
|
|
a := p.A.Marshal()
|
|
ps.A[0] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(a[:32]).Bytes())
|
|
ps.A[1] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(a[32:64]).Bytes())
|
|
ps.A[2] = "1"
|
|
|
|
b := p.B.Marshal()
|
|
ps.B[0][1] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(b[:32]).Bytes())
|
|
ps.B[0][0] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(b[32:64]).Bytes())
|
|
ps.B[1][1] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(b[64:96]).Bytes())
|
|
ps.B[1][0] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(b[96:128]).Bytes())
|
|
ps.B[2][0] = "1"
|
|
ps.B[2][1] = "0"
|
|
|
|
c := p.C.Marshal()
|
|
ps.C[0] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(c[:32]).Bytes())
|
|
ps.C[1] = "0x" + hex.EncodeToString(new(big.Int).SetBytes(c[32:64]).Bytes())
|
|
ps.C[2] = "1"
|
|
|
|
ps.Protocol = "groth"
|
|
|
|
return ps
|
|
}
|
|
|
|
// ProofToJsonHex outputs the Proof i Json format with hexadecimal strings
|
|
func ProofToJsonHex(p *types.Proof) ([]byte, error) {
|
|
ps := ProofToHex(p)
|
|
return json.Marshal(ps)
|
|
}
|
|
|
|
// ParseWitnessBin parses binary file representation of the Witness into the Witness struct
|
|
func ParseWitnessBin(f *os.File) (types.Witness, error) {
|
|
var w types.Witness
|
|
r := bufio.NewReader(f)
|
|
for {
|
|
b := make([]byte, 32)
|
|
n, err := r.Read(b)
|
|
if err == io.EOF {
|
|
return w, nil
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
if n != 32 {
|
|
return nil, fmt.Errorf("error on value format, expected 32 bytes, got %v", n)
|
|
}
|
|
w = append(w, new(big.Int).SetBytes(swapEndianness(b[0:32])))
|
|
}
|
|
}
|
|
|
|
// swapEndianness swaps the order of the bytes in the slice.
|
|
func swapEndianness(b []byte) []byte {
|
|
o := make([]byte, len(b))
|
|
for i := range b {
|
|
o[len(b)-1-i] = b[i]
|
|
}
|
|
return o
|
|
}
|
|
|
|
func readNBytes(r io.Reader, n int) ([]byte, error) {
|
|
b := make([]byte, n)
|
|
_, err := io.ReadFull(r, b)
|
|
if err != nil {
|
|
return b, err
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// ParsePkBin parses binary file representation of the ProvingKey into the ProvingKey struct
|
|
func ParsePkBin(f *os.File) (*types.Pk, error) {
|
|
o := 0
|
|
var pk types.Pk
|
|
r := bufio.NewReader(f)
|
|
|
|
b, err := readNBytes(r, 12)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.NVars = int(binary.LittleEndian.Uint32(b[:4]))
|
|
pk.NPublic = int(binary.LittleEndian.Uint32(b[4:8]))
|
|
pk.DomainSize = int(binary.LittleEndian.Uint32(b[8:12]))
|
|
o += 12
|
|
|
|
b, err = readNBytes(r, 8)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pPolsA := int(binary.LittleEndian.Uint32(b[:4]))
|
|
pPolsB := int(binary.LittleEndian.Uint32(b[4:8]))
|
|
o += 8
|
|
|
|
b, err = readNBytes(r, 20)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pPointsA := int(binary.LittleEndian.Uint32(b[:4]))
|
|
pPointsB1 := int(binary.LittleEndian.Uint32(b[4:8]))
|
|
pPointsB2 := int(binary.LittleEndian.Uint32(b[8:12]))
|
|
pPointsC := int(binary.LittleEndian.Uint32(b[12:16]))
|
|
pPointsHExps := int(binary.LittleEndian.Uint32(b[16:20]))
|
|
o += 20
|
|
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkAlpha1 = new(bn256.G1)
|
|
_, err = pk.VkAlpha1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkBeta1 = new(bn256.G1)
|
|
_, err = pk.VkBeta1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkDelta1 = new(bn256.G1)
|
|
_, err = pk.VkDelta1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkBeta2 = new(bn256.G2)
|
|
_, err = pk.VkBeta2.Unmarshal(fromMont2Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkDelta2 = new(bn256.G2)
|
|
_, err = pk.VkDelta2.Unmarshal(fromMont2Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
o += 448
|
|
if o != pPolsA {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsA, o)
|
|
}
|
|
|
|
// PolsA
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
|
|
o += 4
|
|
polsMap := make(map[int]*big.Int)
|
|
for j := 0; j < keysLength; j++ {
|
|
bK, err := readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := int(binary.LittleEndian.Uint32(bK[:4]))
|
|
o += 4
|
|
|
|
b, err := readNBytes(r, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
polsMap[key] = new(big.Int).SetBytes(fromMont1R(b[:32]))
|
|
o += 32
|
|
}
|
|
pk.PolsA = append(pk.PolsA, polsMap)
|
|
}
|
|
if o != pPolsB {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsB, o)
|
|
}
|
|
// PolsB
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
|
|
o += 4
|
|
polsMap := make(map[int]*big.Int)
|
|
for j := 0; j < keysLength; j++ {
|
|
bK, err := readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := int(binary.LittleEndian.Uint32(bK[:4]))
|
|
o += 4
|
|
|
|
b, err := readNBytes(r, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
polsMap[key] = new(big.Int).SetBytes(fromMont1R(b[:32]))
|
|
o += 32
|
|
}
|
|
pk.PolsB = append(pk.PolsB, polsMap)
|
|
}
|
|
if o != pPointsA {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsA, o)
|
|
}
|
|
// A
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.A = append(pk.A, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsB1 {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB1, o)
|
|
}
|
|
// B1
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.B1 = append(pk.B1, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsB2 {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB2, o)
|
|
}
|
|
// B2
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p2 := new(bn256.G2)
|
|
_, err = p2.Unmarshal(fromMont2Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.B2 = append(pk.B2, p2)
|
|
o += 128
|
|
}
|
|
if o != pPointsC {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsC, o)
|
|
}
|
|
// C
|
|
zb := make([]byte, 64)
|
|
z := new(bn256.G1)
|
|
_, err = z.Unmarshal(zb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.C = append(pk.C, z) // circom behaviour (3x null==["0", "0", "0"])
|
|
pk.C = append(pk.C, z)
|
|
pk.C = append(pk.C, z)
|
|
for i := pk.NPublic + 1; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.C = append(pk.C, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsHExps {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsHExps, o)
|
|
}
|
|
// HExps
|
|
for i := 0; i < pk.DomainSize; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(fromMont1Q(b))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.HExps = append(pk.HExps, p1)
|
|
}
|
|
return &pk, nil
|
|
}
|
|
|
|
func fromMont1Q(m []byte) []byte {
|
|
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
|
|
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
|
|
|
|
x := coordFromMont(a, types.Q)
|
|
y := coordFromMont(b, types.Q)
|
|
if bytes.Equal(x.Bytes(), big.NewInt(1).Bytes()) {
|
|
x = big.NewInt(0)
|
|
}
|
|
if bytes.Equal(y.Bytes(), big.NewInt(1).Bytes()) {
|
|
y = big.NewInt(0)
|
|
}
|
|
|
|
xBytes := x.Bytes()
|
|
yBytes := y.Bytes()
|
|
if len(xBytes) != 32 {
|
|
xBytes = addZPadding(xBytes)
|
|
}
|
|
if len(yBytes) != 32 {
|
|
yBytes = addZPadding(yBytes)
|
|
}
|
|
|
|
var p []byte
|
|
p = append(p, xBytes...)
|
|
p = append(p, yBytes...)
|
|
|
|
return p
|
|
}
|
|
|
|
func fromMont2Q(m []byte) []byte {
|
|
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
|
|
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
|
|
c := new(big.Int).SetBytes(swapEndianness(m[64:96]))
|
|
d := new(big.Int).SetBytes(swapEndianness(m[96:128]))
|
|
|
|
x := coordFromMont(a, types.Q)
|
|
y := coordFromMont(b, types.Q)
|
|
z := coordFromMont(c, types.Q)
|
|
t := coordFromMont(d, types.Q)
|
|
|
|
if bytes.Equal(x.Bytes(), big.NewInt(1).Bytes()) {
|
|
x = big.NewInt(0)
|
|
}
|
|
if bytes.Equal(y.Bytes(), big.NewInt(1).Bytes()) {
|
|
y = big.NewInt(0)
|
|
}
|
|
if bytes.Equal(z.Bytes(), big.NewInt(1).Bytes()) {
|
|
z = big.NewInt(0)
|
|
}
|
|
if bytes.Equal(t.Bytes(), big.NewInt(1).Bytes()) {
|
|
t = big.NewInt(0)
|
|
}
|
|
|
|
xBytes := x.Bytes()
|
|
yBytes := y.Bytes()
|
|
zBytes := z.Bytes()
|
|
tBytes := t.Bytes()
|
|
if len(xBytes) != 32 {
|
|
xBytes = addZPadding(xBytes)
|
|
}
|
|
if len(yBytes) != 32 {
|
|
yBytes = addZPadding(yBytes)
|
|
}
|
|
if len(zBytes) != 32 {
|
|
zBytes = addZPadding(zBytes)
|
|
}
|
|
if len(tBytes) != 32 {
|
|
tBytes = addZPadding(tBytes)
|
|
}
|
|
|
|
var p []byte
|
|
p = append(p, yBytes...) // swap
|
|
p = append(p, xBytes...)
|
|
p = append(p, tBytes...)
|
|
p = append(p, zBytes...)
|
|
|
|
return p
|
|
}
|
|
|
|
func fromMont1R(m []byte) []byte {
|
|
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
|
|
|
|
x := coordFromMont(a, types.R)
|
|
|
|
return x.Bytes()
|
|
}
|
|
|
|
func fromMont2R(m []byte) []byte {
|
|
a := new(big.Int).SetBytes(swapEndianness(m[:32]))
|
|
b := new(big.Int).SetBytes(swapEndianness(m[32:64]))
|
|
c := new(big.Int).SetBytes(swapEndianness(m[64:96]))
|
|
d := new(big.Int).SetBytes(swapEndianness(m[96:128]))
|
|
|
|
x := coordFromMont(a, types.R)
|
|
y := coordFromMont(b, types.R)
|
|
z := coordFromMont(c, types.R)
|
|
t := coordFromMont(d, types.R)
|
|
|
|
var p []byte
|
|
p = append(p, y.Bytes()...) // swap
|
|
p = append(p, x.Bytes()...)
|
|
p = append(p, t.Bytes()...)
|
|
p = append(p, z.Bytes()...)
|
|
|
|
return p
|
|
}
|
|
|
|
func coordFromMont(u, q *big.Int) *big.Int {
|
|
return new(big.Int).Mod(
|
|
new(big.Int).Mul(
|
|
u,
|
|
new(big.Int).ModInverse(
|
|
new(big.Int).Lsh(big.NewInt(1), 256),
|
|
q,
|
|
),
|
|
),
|
|
q,
|
|
)
|
|
}
|
|
|
|
// PkToGoBin converts the ProvingKey (*types.Pk) into binary format defined by
|
|
// go-circom-prover-verifier. PkGoBin is a own go-circom-prover-verifier
|
|
// binary format that allows to go faster when parsing.
|
|
func PkToGoBin(pk *types.Pk) ([]byte, error) {
|
|
var r []byte
|
|
o := 0
|
|
var b [4]byte
|
|
binary.LittleEndian.PutUint32(b[:], uint32(pk.NVars))
|
|
r = append(r, b[:]...)
|
|
|
|
binary.LittleEndian.PutUint32(b[:], uint32(pk.NPublic))
|
|
r = append(r, b[:]...)
|
|
|
|
binary.LittleEndian.PutUint32(b[:], uint32(pk.DomainSize))
|
|
r = append(r, b[:]...)
|
|
o += 12
|
|
|
|
// reserve space for pols (A, B) pos
|
|
b = [4]byte{}
|
|
r = append(r, b[:]...) // 12:16
|
|
r = append(r, b[:]...) // 16:20
|
|
o += 8
|
|
// reserve space for points (A, B1, B2, C, HExps) pos
|
|
r = append(r, b[:]...) // 20:24
|
|
r = append(r, b[:]...) // 24
|
|
r = append(r, b[:]...) // 28
|
|
r = append(r, b[:]...) // 32
|
|
r = append(r, b[:]...) // 36:40
|
|
o += 20
|
|
|
|
pb1 := pk.VkAlpha1.Marshal()
|
|
r = append(r, pb1[:]...)
|
|
pb1 = pk.VkBeta1.Marshal()
|
|
r = append(r, pb1[:]...)
|
|
pb1 = pk.VkDelta1.Marshal()
|
|
r = append(r, pb1[:]...)
|
|
pb2 := pk.VkBeta2.Marshal()
|
|
r = append(r, pb2[:]...)
|
|
pb2 = pk.VkDelta2.Marshal()
|
|
r = append(r, pb2[:]...)
|
|
o += 448
|
|
|
|
// polsA
|
|
binary.LittleEndian.PutUint32(r[12:16], uint32(o))
|
|
for i := 0; i < pk.NVars; i++ {
|
|
binary.LittleEndian.PutUint32(b[:], uint32(len(pk.PolsA[i])))
|
|
r = append(r, b[:]...)
|
|
o += 4
|
|
for j, v := range pk.PolsA[i] {
|
|
binary.LittleEndian.PutUint32(b[:], uint32(j))
|
|
r = append(r, b[:]...)
|
|
r = append(r, addPadding32(v.Bytes())...)
|
|
o += 32 + 4
|
|
}
|
|
}
|
|
// polsB
|
|
binary.LittleEndian.PutUint32(r[16:20], uint32(o))
|
|
for i := 0; i < pk.NVars; i++ {
|
|
binary.LittleEndian.PutUint32(b[:], uint32(len(pk.PolsB[i])))
|
|
r = append(r, b[:]...)
|
|
o += 4
|
|
for j, v := range pk.PolsB[i] {
|
|
binary.LittleEndian.PutUint32(b[:], uint32(j))
|
|
r = append(r, b[:]...)
|
|
r = append(r, addPadding32(v.Bytes())...)
|
|
o += 32 + 4
|
|
}
|
|
}
|
|
// A
|
|
binary.LittleEndian.PutUint32(r[20:24], uint32(o))
|
|
for i := 0; i < pk.NVars; i++ {
|
|
pb1 = pk.A[i].Marshal()
|
|
r = append(r, pb1[:]...)
|
|
o += 64
|
|
}
|
|
// B1
|
|
binary.LittleEndian.PutUint32(r[24:28], uint32(o))
|
|
for i := 0; i < pk.NVars; i++ {
|
|
pb1 = pk.B1[i].Marshal()
|
|
r = append(r, pb1[:]...)
|
|
o += 64
|
|
}
|
|
// B2
|
|
binary.LittleEndian.PutUint32(r[28:32], uint32(o))
|
|
for i := 0; i < pk.NVars; i++ {
|
|
pb2 = pk.B2[i].Marshal()
|
|
r = append(r, pb2[:]...)
|
|
o += 128
|
|
}
|
|
// C
|
|
binary.LittleEndian.PutUint32(r[32:36], uint32(o))
|
|
for i := pk.NPublic + 1; i < pk.NVars; i++ {
|
|
pb1 = pk.C[i].Marshal()
|
|
r = append(r, pb1[:]...)
|
|
o += 64
|
|
}
|
|
// HExps
|
|
binary.LittleEndian.PutUint32(r[36:40], uint32(o))
|
|
for i := 0; i < pk.DomainSize+1; i++ {
|
|
pb1 = pk.HExps[i].Marshal()
|
|
r = append(r, pb1[:]...)
|
|
o += 64
|
|
}
|
|
|
|
return r[:], nil
|
|
}
|
|
|
|
// ParsePkGoBin parses go-circom-prover-verifier binary file representation of
|
|
// the ProvingKey into ProvingKey struct (*types.Pk). PkGoBin is a own
|
|
// go-circom-prover-verifier binary format that allows to go faster when
|
|
// parsing.
|
|
func ParsePkGoBin(f *os.File) (*types.Pk, error) {
|
|
o := 0
|
|
var pk types.Pk
|
|
r := bufio.NewReader(f)
|
|
|
|
b, err := readNBytes(r, 12)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.NVars = int(binary.LittleEndian.Uint32(b[:4]))
|
|
pk.NPublic = int(binary.LittleEndian.Uint32(b[4:8]))
|
|
pk.DomainSize = int(binary.LittleEndian.Uint32(b[8:12]))
|
|
o += 12
|
|
|
|
b, err = readNBytes(r, 8)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pPolsA := int(binary.LittleEndian.Uint32(b[:4]))
|
|
pPolsB := int(binary.LittleEndian.Uint32(b[4:8]))
|
|
o += 8
|
|
|
|
b, err = readNBytes(r, 20)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pPointsA := int(binary.LittleEndian.Uint32(b[:4]))
|
|
pPointsB1 := int(binary.LittleEndian.Uint32(b[4:8]))
|
|
pPointsB2 := int(binary.LittleEndian.Uint32(b[8:12]))
|
|
pPointsC := int(binary.LittleEndian.Uint32(b[12:16]))
|
|
pPointsHExps := int(binary.LittleEndian.Uint32(b[16:20]))
|
|
o += 20
|
|
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkAlpha1 = new(bn256.G1)
|
|
_, err = pk.VkAlpha1.Unmarshal(b)
|
|
if err != nil {
|
|
return &pk, err
|
|
}
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkBeta1 = new(bn256.G1)
|
|
_, err = pk.VkBeta1.Unmarshal(b)
|
|
if err != nil {
|
|
return &pk, err
|
|
}
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkDelta1 = new(bn256.G1)
|
|
_, err = pk.VkDelta1.Unmarshal(b)
|
|
if err != nil {
|
|
return &pk, err
|
|
}
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkBeta2 = new(bn256.G2)
|
|
_, err = pk.VkBeta2.Unmarshal(b)
|
|
if err != nil {
|
|
return &pk, err
|
|
}
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.VkDelta2 = new(bn256.G2)
|
|
_, err = pk.VkDelta2.Unmarshal(b)
|
|
if err != nil {
|
|
return &pk, err
|
|
}
|
|
o += 448
|
|
if o != pPolsA {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsA, o)
|
|
}
|
|
|
|
// PolsA
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
|
|
o += 4
|
|
polsMap := make(map[int]*big.Int)
|
|
for j := 0; j < keysLength; j++ {
|
|
bK, err := readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := int(binary.LittleEndian.Uint32(bK[:4]))
|
|
o += 4
|
|
|
|
b, err := readNBytes(r, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
polsMap[key] = new(big.Int).SetBytes(b[:32])
|
|
o += 32
|
|
}
|
|
pk.PolsA = append(pk.PolsA, polsMap)
|
|
}
|
|
if o != pPolsB {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPolsB, o)
|
|
}
|
|
// PolsB
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
keysLength := int(binary.LittleEndian.Uint32(b[:4]))
|
|
o += 4
|
|
polsMap := make(map[int]*big.Int)
|
|
for j := 0; j < keysLength; j++ {
|
|
bK, err := readNBytes(r, 4)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := int(binary.LittleEndian.Uint32(bK[:4]))
|
|
o += 4
|
|
|
|
b, err := readNBytes(r, 32)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
polsMap[key] = new(big.Int).SetBytes(b[:32])
|
|
o += 32
|
|
}
|
|
pk.PolsB = append(pk.PolsB, polsMap)
|
|
}
|
|
if o != pPointsA {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsA, o)
|
|
}
|
|
// A
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.A = append(pk.A, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsB1 {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB1, o)
|
|
}
|
|
// B1
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.B1 = append(pk.B1, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsB2 {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsB2, o)
|
|
}
|
|
// B2
|
|
for i := 0; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 128)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p2 := new(bn256.G2)
|
|
_, err = p2.Unmarshal(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.B2 = append(pk.B2, p2)
|
|
o += 128
|
|
}
|
|
if o != pPointsC {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsC, o)
|
|
}
|
|
// C
|
|
zb := make([]byte, 64)
|
|
z := new(bn256.G1)
|
|
_, err = z.Unmarshal(zb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.C = append(pk.C, z)
|
|
pk.C = append(pk.C, z)
|
|
pk.C = append(pk.C, z)
|
|
for i := pk.NPublic + 1; i < pk.NVars; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.C = append(pk.C, p1)
|
|
o += 64
|
|
}
|
|
if o != pPointsHExps {
|
|
return nil, fmt.Errorf("Unexpected offset, expected: %v, actual: %v", pPointsHExps, o)
|
|
}
|
|
// HExps
|
|
for i := 0; i < pk.DomainSize+1; i++ {
|
|
b, err = readNBytes(r, 64)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p1 := new(bn256.G1)
|
|
_, err = p1.Unmarshal(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pk.HExps = append(pk.HExps, p1)
|
|
}
|
|
|
|
return &pk, nil
|
|
}
|