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 }