gnark icon indicating copy to clipboard operation
gnark copied to clipboard

Error when generating a proof for a circuit to which I did not add an extra initial public variable

Open ilitteri opened this issue 1 year ago • 1 comments

The next piece of code proves that $x \cdot y = z$ where $x, z$ are public variables and $y$ is a private variable (witness):

func Example() {
    // [Y, Z]
    publicVariables := []fr_bn254.Element{fr_bn254.NewElement(2), fr_bn254.NewElement(6)}
    // [X]
    secretVariables := []fr_bn254.Element{fr_bn254.NewElement(3)

    /* R1CS Building */

    // (X * Y) == Z
    // X is secret
    // Y is public
    // Z is public
    r1cs := cs_bn254.NewR1CS(1)

    // Variables
    _ = r1cs.AddPublicVariable("1") // the ONE_WIRE
    Y := r1cs.AddPublicVariable("Y")
    Z := r1cs.AddPublicVariable("Z")
    X := r1cs.AddSecretVariable("X")

    // Coefficients
    COEFFICIENT_ONE := r1cs.FromInterface(1)

    // Constraints
    // (1 * X) * (1 * Y) == (1 * Z)
    constraint := constraint.R1C{
        L: constraint.LinearExpression{r1cs.MakeTerm(&COEFFICIENT_ONE, X)}, // 1 * X
        R: constraint.LinearExpression{r1cs.MakeTerm(&COEFFICIENT_ONE, Y)}, // 1 * Y
        O: constraint.LinearExpression{r1cs.MakeTerm(&COEFFICIENT_ONE, Z)}, // 1 * Z
    }
    r1cs.AddConstraint(constraint)

    constraints, r := r1cs.GetConstraints()

    for _, r1c := range constraints {
        fmt.Println(r1c.String(r))
    }

    /* Universal SRS Generation */

    pk, vk, _ := groth16.Setup(r1cs)

    /* Proving */

    rightWitness := buildWitnesses(r1cs, publicVariables, secretVariables)

    p, _ := groth16.Prove(r1cs, pk, rightWitness)

    /* Verification */

    publicWitness, _ := rightWitness.Public()

    verifies := groth16.Verify(p, vk, publicWitness)

    fmt.Println("Verifies with the right public values :", verifies == nil)

    wrongPublicVariables := []fr_bn254.Element{fr_bn254.NewElement(1), fr_bn254.NewElement(5)}
    wrongWitness := buildWitnesses(r1cs, wrongPublicVariables, secretVariables)
    wrongPublicWitness, _ := wrongWitness.Public()
    verifies = groth16.Verify(p, vk, wrongPublicWitness)

    fmt.Println("Verifies with the wrong public values :", verifies == nil)
}

For you to be able to run this, you'll need the buildWitness function:

func buildWitnesses(r1cs *cs_bn254.R1CS, publicVariables fr_bn254.Vector, privateVariables fr_bn254.Vector) witness.Witness {
    witnessValues := make(chan any)

    go func() {
        defer close(witnessValues)
        for _, publicVariable := range publicVariables {
            witnessValues <- publicVariable
        }
        for _, privateVariable := range privateVariables {
            witnessValues <- privateVariable
        }
    }()

    witness, err := witness.New(r1cs.CurveID().ScalarField())
    if err != nil {
        log.Fatal(err)
    }

    // -1 because the first variable is the ONE_WIRE.
    // This is a patch for the bug that I'm issuing that let this code work.
    witness.Fill(r1cs.GetNbPublicVariables()-1, r1cs.GetNbSecretVariables(), witnessValues)

    return witness
}

This code works but only because I added manually the following line:

_ = r1cs.AddPublicVariable("1") // the ONE_WIRE

If you remove this line and the -1 in the patch from the line:

witness.Fill(r1cs.GetNbPublicVariables()-1, r1cs.GetNbSecretVariables(), witnessValues)

you'll get the following error when proving:

18:32:36 ERR error="invalid witness size, got 3, expected 2 = 1 (public) + 1 (secret)" backend=groth16 nbConstraints=1

The error says that we are specting 2 variables (1 public and 1 private) when this is wrong. We've already declared 3 variables (2 public and 1 private).

Users mustn't be responsible for handling this like that.

ilitteri avatar Mar 08 '23 17:03 ilitteri