diff --git a/backend/accelerated/icicle/groth16/bls12-377/icicle.go b/backend/accelerated/icicle/groth16/bls12-377/icicle.go index 39c8ecd1b9..caf4ae124f 100644 --- a/backend/accelerated/icicle/groth16/bls12-377/icicle.go +++ b/backend/accelerated/icicle/groth16/bls12-377/icicle.go @@ -822,6 +822,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic proveMu.Lock() defer proveMu.Unlock() + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -875,9 +878,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic <-ckBasisMsmDone runtime.KeepAlive(privateCommittedValues[i]) // host slices must survive until CUDA finishes copying + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/accelerated/icicle/groth16/bls12-381/icicle.go b/backend/accelerated/icicle/groth16/bls12-381/icicle.go index 5dda9ebf14..db07e52b0e 100644 --- a/backend/accelerated/icicle/groth16/bls12-381/icicle.go +++ b/backend/accelerated/icicle/groth16/bls12-381/icicle.go @@ -822,6 +822,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic proveMu.Lock() defer proveMu.Unlock() + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -875,9 +878,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic <-ckBasisMsmDone runtime.KeepAlive(privateCommittedValues[i]) // host slices must survive until CUDA finishes copying + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/accelerated/icicle/groth16/bn254/icicle.go b/backend/accelerated/icicle/groth16/bn254/icicle.go index f0c33bc76c..b202ecf71b 100644 --- a/backend/accelerated/icicle/groth16/bn254/icicle.go +++ b/backend/accelerated/icicle/groth16/bn254/icicle.go @@ -822,6 +822,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic proveMu.Lock() defer proveMu.Unlock() + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -875,9 +878,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic <-ckBasisMsmDone runtime.KeepAlive(privateCommittedValues[i]) // host slices must survive until CUDA finishes copying + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/accelerated/icicle/groth16/bw6-761/icicle.go b/backend/accelerated/icicle/groth16/bw6-761/icicle.go index e557fee455..b6ee5714c5 100644 --- a/backend/accelerated/icicle/groth16/bw6-761/icicle.go +++ b/backend/accelerated/icicle/groth16/bw6-761/icicle.go @@ -811,6 +811,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic proveMu.Lock() defer proveMu.Unlock() + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -864,9 +867,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic <-ckBasisMsmDone runtime.KeepAlive(privateCommittedValues[i]) // host slices must survive until CUDA finishes copying + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/accelerated/icicle/internal/generator/templates/groth16.icicle.go.tmpl b/backend/accelerated/icicle/internal/generator/templates/groth16.icicle.go.tmpl index 559ea3b626..9b2b7c0a25 100644 --- a/backend/accelerated/icicle/internal/generator/templates/groth16.icicle.go.tmpl +++ b/backend/accelerated/icicle/internal/generator/templates/groth16.icicle.go.tmpl @@ -836,6 +836,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic proveMu.Lock() defer proveMu.Unlock() + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -889,9 +892,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, cfg *icic <-ckBasisMsmDone runtime.KeepAlive(privateCommittedValues[i]) // host slices must survive until CUDA finishes copying + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/groth16/bls12-377/prove.go b/backend/groth16/bls12-377/prove.go index 4258042285..41846025aa 100644 --- a/backend/groth16/bls12-377/prove.go +++ b/backend/groth16/bls12-377/prove.go @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc" @@ -164,6 +165,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b // override hints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -179,9 +183,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b return err } + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/groth16/bls12-381/prove.go b/backend/groth16/bls12-381/prove.go index 6e019e8e74..5dc0fcbb81 100644 --- a/backend/groth16/bls12-381/prove.go +++ b/backend/groth16/bls12-381/prove.go @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc" @@ -164,6 +165,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b // override hints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -179,9 +183,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b return err } + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/groth16/bn254/prove.go b/backend/groth16/bn254/prove.go index d4de05c83a..a98d0247df 100644 --- a/backend/groth16/bn254/prove.go +++ b/backend/groth16/bn254/prove.go @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc" @@ -164,6 +165,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b // override hints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -179,9 +183,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b return err } + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/groth16/bw6-761/prove.go b/backend/groth16/bw6-761/prove.go index 163b64d5c6..32b3c3c10b 100644 --- a/backend/groth16/bw6-761/prove.go +++ b/backend/groth16/bw6-761/prove.go @@ -10,6 +10,7 @@ import ( "fmt" "math/big" "runtime" + "sync" "time" "github.com/consensys/gnark-crypto/ecc" @@ -164,6 +165,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b // override hints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -179,9 +183,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b return err } + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/backend/groth16/groth16_test.go b/backend/groth16/groth16_test.go index 027dc388d2..584dfc80be 100644 --- a/backend/groth16/groth16_test.go +++ b/backend/groth16/groth16_test.go @@ -13,6 +13,7 @@ import ( "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/test" + "github.com/stretchr/testify/require" ) func TestCustomHashToField(t *testing.T) { @@ -182,3 +183,58 @@ func getCurves() []ecc.ID { } return gnark.Curves() } + +func TestIndependentCommitments(t *testing.T) { + assert := require.New(t) + ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &independentCommitmentsCircuit{}) + assert.NoError(err) + pk, vk, err := groth16.Setup(ccs) + assert.NoError(err) + witness, err := frontend.NewWitness(independentCommitmentsAssignment(), ecc.BN254.ScalarField()) + assert.NoError(err) + pubWitness, err := witness.Public() + assert.NoError(err) + // the commitment hints may run concurrently, prove a few times to catch + // any interference between them + for i := 0; i < 20; i++ { + proof, err := groth16.Prove(ccs, pk, witness) + assert.NoError(err) + assert.NoError(groth16.Verify(proof, vk, pubWitness), "proof %d", i) + } +} + +// independentCommitmentsCircuit has two commitments which don't depend on each +// other, with enough constraints around them so that the solver computes both +// commitment hints in the same level, in parallel. +type independentCommitmentsCircuit struct { + X [400]frontend.Variable + Y [400]frontend.Variable `gnark:",public"` +} + +func (c *independentCommitmentsCircuit) Define(api frontend.API) error { + for i := 0; i < len(c.X)/2; i++ { + api.AssertIsEqual(api.Mul(c.X[i], c.X[i]), c.Y[i]) + } + cmt1, err := api.(frontend.Committer).Commit(c.X[0], c.X[1]) + if err != nil { + return err + } + for i := len(c.X) / 2; i < len(c.X); i++ { + api.AssertIsEqual(api.Mul(c.X[i], c.X[i]), c.Y[i]) + } + cmt2, err := api.(frontend.Committer).Commit(c.X[2], c.X[3]) + if err != nil { + return err + } + api.AssertIsDifferent(cmt1, cmt2) + return nil +} + +func independentCommitmentsAssignment() *independentCommitmentsCircuit { + var a independentCommitmentsCircuit + for i := range a.X { + a.X[i] = i + 2 + a.Y[i] = (i + 2) * (i + 2) + } + return &a +} diff --git a/backend/plonk/bls12-377/prove.go b/backend/plonk/bls12-377/prove.go index 61df284551..0c763fcffe 100644 --- a/backend/plonk/bls12-377/prove.go +++ b/backend/plonk/bls12-377/prove.go @@ -259,8 +259,9 @@ type instance struct { opt *backend.ProverConfig fs *fiatshamir.Transcript - kzgFoldingHash hash.Hash // for KZG folding - htfFunc hash.Hash // hash to field function + kzgFoldingHash hash.Hash // for KZG folding + htfFunc hash.Hash // hash to field function + htfLock sync.Mutex // bsb22Hint may be called concurrently by the solver // polynomials x []*iop.Polynomial // x stores tracks the polynomial we need @@ -398,9 +399,11 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { return err } + s.htfLock.Lock() s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() + s.htfLock.Unlock() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { nbBuf = s.htfFunc.Size() diff --git a/backend/plonk/bls12-381/prove.go b/backend/plonk/bls12-381/prove.go index e7b63f9061..1451ca773a 100644 --- a/backend/plonk/bls12-381/prove.go +++ b/backend/plonk/bls12-381/prove.go @@ -259,8 +259,9 @@ type instance struct { opt *backend.ProverConfig fs *fiatshamir.Transcript - kzgFoldingHash hash.Hash // for KZG folding - htfFunc hash.Hash // hash to field function + kzgFoldingHash hash.Hash // for KZG folding + htfFunc hash.Hash // hash to field function + htfLock sync.Mutex // bsb22Hint may be called concurrently by the solver // polynomials x []*iop.Polynomial // x stores tracks the polynomial we need @@ -398,9 +399,11 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { return err } + s.htfLock.Lock() s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() + s.htfLock.Unlock() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { nbBuf = s.htfFunc.Size() diff --git a/backend/plonk/bn254/prove.go b/backend/plonk/bn254/prove.go index 91f2020d8a..f62903ffd7 100644 --- a/backend/plonk/bn254/prove.go +++ b/backend/plonk/bn254/prove.go @@ -259,8 +259,9 @@ type instance struct { opt *backend.ProverConfig fs *fiatshamir.Transcript - kzgFoldingHash hash.Hash // for KZG folding - htfFunc hash.Hash // hash to field function + kzgFoldingHash hash.Hash // for KZG folding + htfFunc hash.Hash // hash to field function + htfLock sync.Mutex // bsb22Hint may be called concurrently by the solver // polynomials x []*iop.Polynomial // x stores tracks the polynomial we need @@ -398,9 +399,11 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { return err } + s.htfLock.Lock() s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() + s.htfLock.Unlock() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { nbBuf = s.htfFunc.Size() diff --git a/backend/plonk/bw6-761/prove.go b/backend/plonk/bw6-761/prove.go index 8598631c16..42d698fa07 100644 --- a/backend/plonk/bw6-761/prove.go +++ b/backend/plonk/bw6-761/prove.go @@ -259,8 +259,9 @@ type instance struct { opt *backend.ProverConfig fs *fiatshamir.Transcript - kzgFoldingHash hash.Hash // for KZG folding - htfFunc hash.Hash // hash to field function + kzgFoldingHash hash.Hash // for KZG folding + htfFunc hash.Hash // hash to field function + htfLock sync.Mutex // bsb22Hint may be called concurrently by the solver // polynomials x []*iop.Polynomial // x stores tracks the polynomial we need @@ -398,9 +399,11 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { return err } + s.htfLock.Lock() s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() + s.htfLock.Unlock() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { nbBuf = s.htfFunc.Size() diff --git a/backend/plonk/plonk_test.go b/backend/plonk/plonk_test.go index 4fc939dd58..27e77f1b31 100644 --- a/backend/plonk/plonk_test.go +++ b/backend/plonk/plonk_test.go @@ -380,3 +380,60 @@ func getCurves() []ecc.ID { } return gnark.Curves() } + +func TestIndependentCommitments(t *testing.T) { + assert := require.New(t) + ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &independentCommitmentsCircuit{}) + assert.NoError(err) + srs, srsLagrange, err := unsafekzg.NewSRS(ccs) + assert.NoError(err) + pk, vk, err := plonk.Setup(ccs, srs, srsLagrange) + assert.NoError(err) + witness, err := frontend.NewWitness(independentCommitmentsAssignment(), ecc.BN254.ScalarField()) + assert.NoError(err) + pubWitness, err := witness.Public() + assert.NoError(err) + // the commitment hints may run concurrently, prove a few times to catch + // any interference between them + for i := 0; i < 20; i++ { + proof, err := plonk.Prove(ccs, pk, witness) + assert.NoError(err) + assert.NoError(plonk.Verify(proof, vk, pubWitness), "proof %d", i) + } +} + +// independentCommitmentsCircuit has two commitments which don't depend on each +// other, with enough constraints around them so that the solver computes both +// commitment hints in the same level, in parallel. +type independentCommitmentsCircuit struct { + X [400]frontend.Variable + Y [400]frontend.Variable `gnark:",public"` +} + +func (c *independentCommitmentsCircuit) Define(api frontend.API) error { + for i := 0; i < len(c.X)/2; i++ { + api.AssertIsEqual(api.Mul(c.X[i], c.X[i]), c.Y[i]) + } + cmt1, err := api.(frontend.Committer).Commit(c.X[0], c.X[1]) + if err != nil { + return err + } + for i := len(c.X) / 2; i < len(c.X); i++ { + api.AssertIsEqual(api.Mul(c.X[i], c.X[i]), c.Y[i]) + } + cmt2, err := api.(frontend.Committer).Commit(c.X[2], c.X[3]) + if err != nil { + return err + } + api.AssertIsDifferent(cmt1, cmt2) + return nil +} + +func independentCommitmentsAssignment() *independentCommitmentsCircuit { + var a independentCommitmentsCircuit + for i := range a.X { + a.X[i] = i + 2 + a.Y[i] = (i + 2) * (i + 2) + } + return &a +} diff --git a/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl index bbd73526bb..536b6e34b6 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl @@ -3,6 +3,7 @@ import ( "fmt" "runtime" "math/big" + "sync" "time" {{- template "import_fr" . }} @@ -158,6 +159,9 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b // override hints bsb22ID := solver.GetHintID(fcs.Bsb22CommitmentComputePlaceholder) + // the solver may run the hints of independent commitments concurrently, + // the hash function is shared between them so it needs a lock + var htfLock sync.Mutex solverOpts = append(solverOpts, solver.OverrideHint(bsb22ID, func(_ *big.Int, in []*big.Int, out []*big.Int) error { i := int(in[0].Int64()) in = in[1:] @@ -173,9 +177,11 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b return err } + htfLock.Lock() opt.HashToFieldFn.Write(constraint.SerializeCommitment(proof.Commitments[i].Marshal(), hashed, (fr.Bits-1)/8+1)) hashBts := opt.HashToFieldFn.Sum(nil) opt.HashToFieldFn.Reset() + htfLock.Unlock() nbBuf := fr.Bytes if opt.HashToFieldFn.Size() < fr.Bytes { nbBuf = opt.HashToFieldFn.Size() diff --git a/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl b/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl index 407eb58eb9..1634b432c9 100644 --- a/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/plonk/plonk.prove.go.tmpl @@ -249,6 +249,7 @@ type instance struct { fs *fiatshamir.Transcript kzgFoldingHash hash.Hash // for KZG folding htfFunc hash.Hash // hash to field function + htfLock sync.Mutex // bsb22Hint may be called concurrently by the solver // polynomials x []*iop.Polynomial // x stores tracks the polynomial we need @@ -386,9 +387,11 @@ func (s *instance) bsb22Hint(_ *big.Int, ins, outs []*big.Int) error { return err } + s.htfLock.Lock() s.htfFunc.Write(s.proof.Bsb22Commitments[commDepth].Marshal()) hashBts := s.htfFunc.Sum(nil) s.htfFunc.Reset() + s.htfLock.Unlock() nbBuf := fr.Bytes if s.htfFunc.Size() < fr.Bytes { nbBuf = s.htfFunc.Size()