func Prove(r1cs *cs.R1CS, pk *ProvingKey, witness bls12_381witness.Witness, opt backend.ProverOption) (*Proof, error) {
	if len(witness) != int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables) {
		return nil, fmt.Errorf("invalid witness size, got %d, expected %d = %d (public - ONE_WIRE) + %d (secret)", len(witness), int(r1cs.NbPublicVariables-1+r1cs.NbSecretVariables), r1cs.NbPublicVariables, r1cs.NbSecretVariables)
	}
	a := make([]fr.Element, len(r1cs.Constraints), pk.Domain.Cardinality)
	b := make([]fr.Element, len(r1cs.Constraints), pk.Domain.Cardinality)
	c := make([]fr.Element, len(r1cs.Constraints), pk.Domain.Cardinality)
	var wireValues []fr.Element
	var err error
	if wireValues, err = r1cs.Solve(witness, a, b, c, opt); err != nil {
		if !opt.Force {
			return nil, err
		} else {
			var r fr.Element
			_, _ = r.SetRandom()
			for i := r1cs.NbPublicVariables + r1cs.NbSecretVariables; i < len(wireValues); i++ {
				wireValues[i] = r
				r.Double(&r)
			}
		}
	}
	utils.Parallelize(len(wireValues), func(start, end int) {
		for i := start; i < end; i++ {
			wireValues[i].FromMont()
		}
	})
	var h []fr.Element
	chHDone := make(chan struct{}, 1)
	go func() {
		h = computeH(a, b, c, &pk.Domain)
		a = nil
		b = nil
		c = nil
		chHDone <- struct{}{}
	}()
	var wireValuesA, wireValuesB []fr.Element
	chWireValuesA, chWireValuesB := make(chan struct{}, 1), make(chan struct{}, 1)
	go func() {
		wireValuesA = make([]fr.Element, len(wireValues)-int(pk.NbInfinityA))
		for i, j := 0, 0; j < len(wireValuesA); i++ {
			if pk.InfinityA[i] {
				continue
			}
			wireValuesA[j] = wireValues[i]
			j++
		}
		close(chWireValuesA)
	}()
	go func() {
		wireValuesB = make([]fr.Element, len(wireValues)-int(pk.NbInfinityB))
		for i, j := 0, 0; j < len(wireValuesB); i++ {
			if pk.InfinityB[i] {
				continue
			}
			wireValuesB[j] = wireValues[i]
			j++
		}
		close(chWireValuesB)
	}()
	var r, s big.Int
	var _r, _s, _kr fr.Element
	if _, err := _r.SetRandom(); err != nil {
		return nil, err
	}
	if _, err := _s.SetRandom(); err != nil {
		return nil, err
	}
	_kr.Mul(&_r, &_s).Neg(&_kr)
	_r.FromMont()
	_s.FromMont()
	_kr.FromMont()
	_r.ToBigInt(&r)
	_s.ToBigInt(&s)
	deltas := curve.BatchScalarMultiplicationG1(&pk.G1.Delta, []fr.Element{_r, _s, _kr})
	proof := &Proof{}
	var bs1, ar curve.G1Jac
	n := runtime.NumCPU()
	chBs1Done := make(chan error, 1)
	computeBS1 := func() {
		<-chWireValuesB
		if _, err := bs1.MultiExp(pk.G1.B, wireValuesB, ecc.MultiExpConfig{NbTasks: n / 2}); err != nil {
			chBs1Done <- err
			close(chBs1Done)
			return
		}
		bs1.AddMixed(&pk.G1.Beta)
		bs1.AddMixed(&deltas[1])
		chBs1Done <- nil
	}
	chArDone := make(chan error, 1)
	computeAR1 := func() {
		<-chWireValuesA
		if _, err := ar.MultiExp(pk.G1.A, wireValuesA, ecc.MultiExpConfig{NbTasks: n / 2}); err != nil {
			chArDone <- err
			close(chArDone)
			return
		}
		ar.AddMixed(&pk.G1.Alpha)
		ar.AddMixed(&deltas[0])
		proof.Ar.FromJacobian(&ar)
		chArDone <- nil
	}
	chKrsDone := make(chan error, 1)
	computeKRS := func() {
		var krs, krs2, p1 curve.G1Jac
		chKrs2Done := make(chan error, 1)
		go func() {
			_, err := krs2.MultiExp(pk.G1.Z, h, ecc.MultiExpConfig{NbTasks: n / 2})
			chKrs2Done <- err
		}()
		if _, err := krs.MultiExp(pk.G1.K, wireValues[r1cs.NbPublicVariables:], ecc.MultiExpConfig{NbTasks: n / 2}); err != nil {
			chKrsDone <- err
			return
		}
		krs.AddMixed(&deltas[2])
		n := 3
		for n != 0 {
			select {
			case err := <-chKrs2Done:
				if err != nil {
					chKrsDone <- err
					return
				}
				krs.AddAssign(&krs2)
			case err := <-chArDone:
				if err != nil {
					chKrsDone <- err
					return
				}
				p1.ScalarMultiplication(&ar, &s)
				krs.AddAssign(&p1)
			case err := <-chBs1Done:
				if err != nil {
					chKrsDone <- err
					return
				}
				p1.ScalarMultiplication(&bs1, &r)
				krs.AddAssign(&p1)
			}
			n--
		}
		proof.Krs.FromJacobian(&krs)
		chKrsDone <- nil
	}
	computeBS2 := func() error {
		var Bs, deltaS curve.G2Jac
		nbTasks := n
		if nbTasks <= 16 {
			nbTasks *= 2
		}
		<-chWireValuesB
		if _, err := Bs.MultiExp(pk.G2.B, wireValuesB, ecc.MultiExpConfig{NbTasks: nbTasks}); err != nil {
			return err
		}
		deltaS.FromAffine(&pk.G2.Delta)
		deltaS.ScalarMultiplication(&deltaS, &s)
		Bs.AddAssign(&deltaS)
		Bs.AddMixed(&pk.G2.Beta)
		proof.Bs.FromJacobian(&Bs)
		return nil
	}
	<-chHDone
	go computeKRS()
	go computeAR1()
	go computeBS1()
	if err := computeBS2(); err != nil {
		return nil, err
	}
	if err := <-chKrsDone; err != nil {
		return nil, err
	}
	return proof, nil
}
Prove generates the proof of knoweldge of a r1cs with full witness (secret + public part).
package groth16
import (
	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
	curve "github.com/consensys/gnark-crypto/ecc/bls12-381"
	"github.com/consensys/gnark/internal/backend/bls12-381/cs"
	"github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft"
	"fmt"
	"github.com/consensys/gnark-crypto/ecc"
	"github.com/consensys/gnark/backend"
	bls12_381witness "github.com/consensys/gnark/internal/backend/bls12-381/witness"
	"github.com/consensys/gnark/internal/utils"
	"math/big"
	"runtime"
)
type Proof struct {
	Ar, Krs curve.G1Affine
	Bs      curve.G2Affine
}
func (proof *Proof) isValid() bool {
	return proof.Ar.IsInSubGroup() && proof.Krs.IsInSubGroup() && proof.Bs.IsInSubGroup()
}
func (proof *Proof) CurveID() ecc.ID {
	return curve.ID
}