diff --git a/backend/groth16/bellman_test.go b/backend/groth16/bellman_test.go index f8bef569c..b47a6244c 100644 --- a/backend/groth16/bellman_test.go +++ b/backend/groth16/bellman_test.go @@ -24,62 +24,62 @@ func TestVerifyBellmanProof(t *testing.T) { ok bool }{ { - "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", true, }, { - "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", false, }, { - "hwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bhwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bo5ViaDBdO7ZBxAhLSe5k/5TFQyF5Lv7KN2tLKnwgoWMqB16OL8WdbePIwTCuPtJNAFKoTZylLDbSf02kckMcZQDPF9iGh+JC99Pio74vDpwTEjUx5tQ99gNQwxULtztsqDRsPnEvKvLmsxHt8LQVBkEBm2PBJFY+OXf1MNW021viDBpR10mX4WQ6zrsGL5L0GY4cwf4tlbh+Obit+LnN/SQTnREf8fPpdKZ1sa/ui3pGi8lMT6io4D7Ujlwx2RdChwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bkBF+isfMf77HCEGsZANw0hSrO2FGg14Sl26xLAIohdaW8O7gEaag8JdVAZ3OVLd5Df1NkZBEr753Xb8WwaXsJjE7qxwINL1KdqA4+EiYW4edb7+a9bbBeOPtb67ZxmFqAAAAAoMkzUv+KG8WoXszZI5NNMrbMLBDYP/xHunVgSWcix/kBrGlNozv1uFr0cmYZiij3YqToYs+EZa3dl2ILHx7H1n+b+Bjky/td2QduHVtf5t/Z9sKCfr+vOn12zVvOVz/6wAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "hwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bhwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bo5ViaDBdO7ZBxAhLSe5k/5TFQyF5Lv7KN2tLKnwgoWMqB16OL8WdbePIwTCuPtJNAFKoTZylLDbSf02kckMcZQDPF9iGh+JC99Pio74vDpwTEjUx5tQ99gNQwxULtztsqDRsPnEvKvLmsxHt8LQVBkEBm2PBJFY+OXf1MNW021viDBpR10mX4WQ6zrsGL5L0GY4cwf4tlbh+Obit+LnN/SQTnREf8fPpdKZ1sa/ui3pGi8lMT6io4D7Ujlwx2RdChwk883gUlTKCyXYA6XWZa8H9/xKIYZaJ0xEs0M5hQOMxiGpxocuX/8maSDmeCk3bkBF+isfMf77HCEGsZANw0hSrO2FGg14Sl26xLAIohdaW8O7gEaag8JdVAZ3OVLd5Df1NkZBEr753Xb8WwaXsJjE7qxwINL1KdqA4+EiYW4edb7+a9bbBeOPtb67ZxmFqAAAAAoMkzUv+KG8WoXszZI5NNMrbMLBDYP/xHunVgSWcix/kBrGlNozv1uFr0cmYZiij3YqToYs+EZa3dl2ILHx7H1n+b+Bjky/td2QduHVtf5t/Z9sKCfr+vOn12zVvOVz/6wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "lvQLU/KqgFhsLkt/5C/scqs7nWR+eYtyPdWiLVBux9GblT4AhHYMdCgwQfSJcudvsgV6fXoK+DUSRgJ++Nqt+Wvb7GlYlHpxCysQhz26TTu8Nyo7zpmVPH92+UYmbvbQCSvX2BhWtvkfHmqDVjmSIQ4RUMfeveA1KZbSf999NE4qKK8Do+8oXcmTM4LZVmh1rlyqznIdFXPN7x3pD4E0gb6/y69xtWMChv9654FMg05bAdueKt9uA4BEcAbpkdHF", "cmzVCcRVnckw3QUPhmG4Bkppeg4K50oDQwQ9EH+Fq1s=", false, }, { - "kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6knkzSwcsialcheg69eZYPK8EzKRVI5FrRHKi8rgB+R5jyPV70ejmYEx1neTmfYKODRmARr/ld6pZTzBWYDfrCkiS1QB+3q3M08OQgYcLzs/vjW4epetDCmk0K1CEGcWdh7yLzdqr7HHQNOpZI8mdj/7lR0IBqB9zvRfyTr+guUG22kZo4y2KINDp272xGglKEeTglTxyDUriZJNF/+T6F8w70MR/rV+flvuo6EJ0+HA+A2ZnBbTjOIl9wjisBV+0kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6jgld4oAppAOzvQ7eoIx2tbuuKVSdbJm65KDxl/T+boaYnjRm3omdETYnYRk3HAhrAeWpefX+dM/k7PrcheInnxHUyjzSzqlN03xYjg28kdda9FZJaVsQKqdEJ/St9ivXAAAAAZae/nTwyDn5u+4WkhZ76991cGB/ymyGpXziT0bwS86pRw/AcbpzXmzK+hq+kvrvpwAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6knkzSwcsialcheg69eZYPK8EzKRVI5FrRHKi8rgB+R5jyPV70ejmYEx1neTmfYKODRmARr/ld6pZTzBWYDfrCkiS1QB+3q3M08OQgYcLzs/vjW4epetDCmk0K1CEGcWdh7yLzdqr7HHQNOpZI8mdj/7lR0IBqB9zvRfyTr+guUG22kZo4y2KINDp272xGglKEeTglTxyDUriZJNF/+T6F8w70MR/rV+flvuo6EJ0+HA+A2ZnBbTjOIl9wjisBV+0kYYCAS8vM2T99GeCr4toQ+iQzvl5fI89mPrncYqx3C1d75BQbFk8LMtcnLWwntd6jgld4oAppAOzvQ7eoIx2tbuuKVSdbJm65KDxl/T+boaYnjRm3omdETYnYRk3HAhrAeWpefX+dM/k7PrcheInnxHUyjzSzqlN03xYjg28kdda9FZJaVsQKqdEJ/St9ivXAAAAAZae/nTwyDn5u+4WkhZ76991cGB/ymyGpXziT0bwS86pRw/AcbpzXmzK+hq+kvrvpwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "sStVLdyxqInmv76iaNnRFB464lGq48iVeqYWSi2linE9DST0fTNhxSnvSXAoPpt8tFsanj5vPafC+ij/Fh98dOUlMbO42bf280pOZ4lm+zr63AWUpOOIugST+S6pq9zeB0OHp2NY8XFmriOEKhxeabhuV89ljqCDjlhXBeNZwM5zti4zg89Hd8TbKcw46jAsjIJe2Siw3Th7ELQQKR5ucX50f0GISmnOSceePPdvjbGJ8fSFOnSmSp8dK7uyehrU", "", true, }, { - "mY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWmY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWpf+4uOyv3gPZe54SYGM4pfhteqJpwFQxdlpwXWyYxMTNaSLDj8VtSn/EJaSu+P6nFmWsda3mTYUPYMZzWE4hMqpDgFPcJhw3prArMThDPbR3Hx7E6NRAAR0LqcrdtsbDqu2T0tto1rpnFILdvHL4PqEUfTmF2mkM+DKj7lKwvvZUbukqBwLrnnbdfyqZJryzGAMIa2JvMEMYszGsYyiPXZvYx6Luk54oWOlOrwEKrCY4NMPwch6DbFq6KpnNSQwOmY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWpgRYCz7wpjk57X+NGJmo85tYKc+TNa1rT4/DxG9v6SHkpXmmPeHhzIIW8MOdkFjxB5o6Qn8Fa0c6Tt6br2gzkrGr1eK5/+RiIgEzVhcRrqdY/p7PLmKXqawrEvIv9QZ3AAAAAoo8rTzcIp5QvF3USzv2Lz99z43CPVkjHB1ejzj/SjzKNa54GiDzHoCoAL0xKLjRSqeL98AF0V1+cRI8FwJjOcMgf0gDmjzwiv3ppbPZKqJR7Go+57k02670lfG6s1MM0AAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "mY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWmY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWpf+4uOyv3gPZe54SYGM4pfhteqJpwFQxdlpwXWyYxMTNaSLDj8VtSn/EJaSu+P6nFmWsda3mTYUPYMZzWE4hMqpDgFPcJhw3prArMThDPbR3Hx7E6NRAAR0LqcrdtsbDqu2T0tto1rpnFILdvHL4PqEUfTmF2mkM+DKj7lKwvvZUbukqBwLrnnbdfyqZJryzGAMIa2JvMEMYszGsYyiPXZvYx6Luk54oWOlOrwEKrCY4NMPwch6DbFq6KpnNSQwOmY//hEITCBCZUJUN/wsOlw1iUSSOESL6PFSbN1abGK80t5jPNICNlPuSorio4mmWpgRYCz7wpjk57X+NGJmo85tYKc+TNa1rT4/DxG9v6SHkpXmmPeHhzIIW8MOdkFjxB5o6Qn8Fa0c6Tt6br2gzkrGr1eK5/+RiIgEzVhcRrqdY/p7PLmKXqawrEvIv9QZ3AAAAAoo8rTzcIp5QvF3USzv2Lz99z43CPVkjHB1ejzj/SjzKNa54GiDzHoCoAL0xKLjRSqeL98AF0V1+cRI8FwJjOcMgf0gDmjzwiv3ppbPZKqJR7Go+57k02670lfG6s1MM0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "g53N8ecorvG2sDgNv8D7quVhKMIIpdP9Bqk/8gmV5cJ5Rhk9gKvb4F0ll8J/ZZJVqa27OyciJwx6lym6QpVK9q1ASrqio7rD5POMDGm64Iay/ixXXn+//F+uKgDXADj9AySri2J1j3qEkqqe3kxKthw94DzAfUBPncHfTPazVtE48AfzB1KWZA7Vf/x/3phYs4ckcP7ZrdVViJVLbUgFy543dpKfEH2MD30ZLLYRhw8SatRCyIJuTZcMlluEKG+d", "aZ8tqrOeEJKt4AMqiRF/WJhIKTDC0HeDTgiJVLZ8OEs=", true, }, { - "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", true, }, { - "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", true, }, { - "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", true, }, { - "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", "", false, }, { - "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", false, }, { - "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", false, }, { - "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", false, }, diff --git a/backend/groth16/bls12-377/commitment_test.go b/backend/groth16/bls12-377/commitment_test.go index f18eefb83..ddc5a295b 100644 --- a/backend/groth16/bls12-377/commitment_test.go +++ b/backend/groth16/bls12-377/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bls12-377/marshal.go b/backend/groth16/bls12-377/marshal.go index ee8facd1e..d0e0f295c 100644 --- a/backend/groth16/bls12-377/marshal.go +++ b/backend/groth16/bls12-377/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bls12-377/marshal_test.go b/backend/groth16/bls12-377/marshal_test.go index 2abaf2340..1859b21e0 100644 --- a/backend/groth16/bls12-377/marshal_test.go +++ b/backend/groth16/bls12-377/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bls12-377/prove.go b/backend/groth16/bls12-377/prove.go index e79d9e835..47c715ada 100644 --- a/backend/groth16/bls12-377/prove.go +++ b/backend/groth16/bls12-377/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-377/fr" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bls12-377/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bls12-377/setup.go b/backend/groth16/bls12-377/setup.go index 97c88ba3c..5b6b3af78 100644 --- a/backend/groth16/bls12-377/setup.go +++ b/backend/groth16/bls12-377/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bls12-377/verify.go b/backend/groth16/bls12-377/verify.go index 03da861b2..2a09746de 100644 --- a/backend/groth16/bls12-377/verify.go +++ b/backend/groth16/bls12-377/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bls12-381/commitment_test.go b/backend/groth16/bls12-381/commitment_test.go index 8f6882aeb..150dcccea 100644 --- a/backend/groth16/bls12-381/commitment_test.go +++ b/backend/groth16/bls12-381/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bls12-381/marshal.go b/backend/groth16/bls12-381/marshal.go index ff47b52ad..8a34d864f 100644 --- a/backend/groth16/bls12-381/marshal.go +++ b/backend/groth16/bls12-381/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bls12-381/marshal_test.go b/backend/groth16/bls12-381/marshal_test.go index 32201fdfc..83b200899 100644 --- a/backend/groth16/bls12-381/marshal_test.go +++ b/backend/groth16/bls12-381/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bls12-381/prove.go b/backend/groth16/bls12-381/prove.go index 37dd0a0d5..671092603 100644 --- a/backend/groth16/bls12-381/prove.go +++ b/backend/groth16/bls12-381/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bls12-381/setup.go b/backend/groth16/bls12-381/setup.go index caf1e9d48..5c2f198ff 100644 --- a/backend/groth16/bls12-381/setup.go +++ b/backend/groth16/bls12-381/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bls12-381/verify.go b/backend/groth16/bls12-381/verify.go index 39d3ba643..fc7f40bfc 100644 --- a/backend/groth16/bls12-381/verify.go +++ b/backend/groth16/bls12-381/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bls24-315/commitment_test.go b/backend/groth16/bls24-315/commitment_test.go index 0f626448b..66bc8e7eb 100644 --- a/backend/groth16/bls24-315/commitment_test.go +++ b/backend/groth16/bls24-315/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bls24-315/marshal.go b/backend/groth16/bls24-315/marshal.go index 614647c77..32cbca836 100644 --- a/backend/groth16/bls24-315/marshal.go +++ b/backend/groth16/bls24-315/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bls24-315/marshal_test.go b/backend/groth16/bls24-315/marshal_test.go index c4115cace..d7e5e2d93 100644 --- a/backend/groth16/bls24-315/marshal_test.go +++ b/backend/groth16/bls24-315/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bls24-315/prove.go b/backend/groth16/bls24-315/prove.go index e3c3e560a..cb846a983 100644 --- a/backend/groth16/bls24-315/prove.go +++ b/backend/groth16/bls24-315/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-315/fr" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bls24-315/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bls24-315/setup.go b/backend/groth16/bls24-315/setup.go index 19f1ed2e3..6b3f19399 100644 --- a/backend/groth16/bls24-315/setup.go +++ b/backend/groth16/bls24-315/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bls24-315/verify.go b/backend/groth16/bls24-315/verify.go index 28a66d60c..7680a54d1 100644 --- a/backend/groth16/bls24-315/verify.go +++ b/backend/groth16/bls24-315/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bls24-317/commitment_test.go b/backend/groth16/bls24-317/commitment_test.go index bfb2fc578..99a80407e 100644 --- a/backend/groth16/bls24-317/commitment_test.go +++ b/backend/groth16/bls24-317/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bls24-317/marshal.go b/backend/groth16/bls24-317/marshal.go index 00e022242..c1aa622e0 100644 --- a/backend/groth16/bls24-317/marshal.go +++ b/backend/groth16/bls24-317/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bls24-317/marshal_test.go b/backend/groth16/bls24-317/marshal_test.go index e926a1b45..b105bc218 100644 --- a/backend/groth16/bls24-317/marshal_test.go +++ b/backend/groth16/bls24-317/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bls24-317/prove.go b/backend/groth16/bls24-317/prove.go index 59651832c..55a4a39a1 100644 --- a/backend/groth16/bls24-317/prove.go +++ b/backend/groth16/bls24-317/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bls24-317/fr" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/fft" "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bls24-317/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bls24-317/setup.go b/backend/groth16/bls24-317/setup.go index 7a051e02d..53628f4c5 100644 --- a/backend/groth16/bls24-317/setup.go +++ b/backend/groth16/bls24-317/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bls24-317/verify.go b/backend/groth16/bls24-317/verify.go index de18d4409..d01ad203e 100644 --- a/backend/groth16/bls24-317/verify.go +++ b/backend/groth16/bls24-317/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bn254/commitment_test.go b/backend/groth16/bn254/commitment_test.go index 759501ebe..9f29b5d3b 100644 --- a/backend/groth16/bn254/commitment_test.go +++ b/backend/groth16/bn254/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bn254/marshal.go b/backend/groth16/bn254/marshal.go index 90dd38d8a..d1c6ba186 100644 --- a/backend/groth16/bn254/marshal.go +++ b/backend/groth16/bn254/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bn254/marshal_test.go b/backend/groth16/bn254/marshal_test.go index a62d4297f..d59c54e85 100644 --- a/backend/groth16/bn254/marshal_test.go +++ b/backend/groth16/bn254/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bn254/prove.go b/backend/groth16/bn254/prove.go index b9dd9f0cd..5f0d41313 100644 --- a/backend/groth16/bn254/prove.go +++ b/backend/groth16/bn254/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" "github.com/consensys/gnark-crypto/ecc/bn254/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bn254/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bn254/setup.go b/backend/groth16/bn254/setup.go index b9fd6a056..13ddcd61d 100644 --- a/backend/groth16/bn254/setup.go +++ b/backend/groth16/bn254/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bn254/solidity.go b/backend/groth16/bn254/solidity.go index 401ecd776..47793bdf7 100644 --- a/backend/groth16/bn254/solidity.go +++ b/backend/groth16/bn254/solidity.go @@ -98,16 +98,17 @@ contract Verifier { {{- if gt $numCommitments 0 }} // Pedersen G point in G2 in powers of i - uint256 constant PEDERSEN_G_X_0 = {{ (fpstr .Vk.CommitmentKey.G.X.A0) }}; - uint256 constant PEDERSEN_G_X_1 = {{ (fpstr .Vk.CommitmentKey.G.X.A1) }}; - uint256 constant PEDERSEN_G_Y_0 = {{ (fpstr .Vk.CommitmentKey.G.Y.A0) }}; - uint256 constant PEDERSEN_G_Y_1 = {{ (fpstr .Vk.CommitmentKey.G.Y.A1) }}; - - // Pedersen GRootSigmaNeg point in G2 in powers of i - uint256 constant PEDERSEN_GROOTSIGMANEG_X_0 = {{ (fpstr .Vk.CommitmentKey.GRootSigmaNeg.X.A0) }}; - uint256 constant PEDERSEN_GROOTSIGMANEG_X_1 = {{ (fpstr .Vk.CommitmentKey.GRootSigmaNeg.X.A1) }}; - uint256 constant PEDERSEN_GROOTSIGMANEG_Y_0 = {{ (fpstr .Vk.CommitmentKey.GRootSigmaNeg.Y.A0) }}; - uint256 constant PEDERSEN_GROOTSIGMANEG_Y_1 = {{ (fpstr .Vk.CommitmentKey.GRootSigmaNeg.Y.A1) }}; + {{- $cmtVk0 := index .Vk.CommitmentKeys 0 }} + uint256 constant PEDERSEN_G_X_0 = {{ (fpstr $cmtVk0.G.X.A0) }}; + uint256 constant PEDERSEN_G_X_1 = {{ (fpstr $cmtVk0.G.X.A1) }}; + uint256 constant PEDERSEN_G_Y_0 = {{ (fpstr $cmtVk0.G.Y.A0) }}; + uint256 constant PEDERSEN_G_Y_1 = {{ (fpstr $cmtVk0.G.Y.A1) }}; + + // Pedersen GSigma point in G2 in powers of i + uint256 constant PEDERSEN_GSIGMA_X_0 = {{ (fpstr $cmtVk0.GSigma.X.A0) }}; + uint256 constant PEDERSEN_GSIGMA_X_1 = {{ (fpstr $cmtVk0.GSigma.X.A1) }}; + uint256 constant PEDERSEN_GSIGMA_Y_0 = {{ (fpstr $cmtVk0.GSigma.Y.A0) }}; + uint256 constant PEDERSEN_GSIGMA_Y_1 = {{ (fpstr $cmtVk0.GSigma.Y.A1) }}; {{- end }} // Constant and public input points @@ -578,16 +579,16 @@ contract Verifier { // Commitments pairings[ 0] = commitments[0]; pairings[ 1] = commitments[1]; - pairings[ 2] = PEDERSEN_G_X_1; - pairings[ 3] = PEDERSEN_G_X_0; - pairings[ 4] = PEDERSEN_G_Y_1; - pairings[ 5] = PEDERSEN_G_Y_0; + pairings[ 2] = PEDERSEN_GSIGMA_X_1; + pairings[ 3] = PEDERSEN_GSIGMA_X_0; + pairings[ 4] = PEDERSEN_GSIGMA_Y_1; + pairings[ 5] = PEDERSEN_GSIGMA_Y_0; pairings[ 6] = Px; pairings[ 7] = Py; - pairings[ 8] = PEDERSEN_GROOTSIGMANEG_X_1; - pairings[ 9] = PEDERSEN_GROOTSIGMANEG_X_0; - pairings[10] = PEDERSEN_GROOTSIGMANEG_Y_1; - pairings[11] = PEDERSEN_GROOTSIGMANEG_Y_0; + pairings[ 8] = PEDERSEN_G_X_1; + pairings[ 9] = PEDERSEN_G_X_0; + pairings[10] = PEDERSEN_G_Y_1; + pairings[11] = PEDERSEN_G_Y_0; // Verify pedersen commitments bool success; @@ -729,15 +730,15 @@ contract Verifier { let f := mload(0x40) calldatacopy(f, commitments, 0x40) // Copy Commitments - mstore(add(f, 0x40), PEDERSEN_G_X_1) - mstore(add(f, 0x60), PEDERSEN_G_X_0) - mstore(add(f, 0x80), PEDERSEN_G_Y_1) - mstore(add(f, 0xa0), PEDERSEN_G_Y_0) + mstore(add(f, 0x40), PEDERSEN_GSIGMA_X_1) + mstore(add(f, 0x60), PEDERSEN_GSIGMA_X_0) + mstore(add(f, 0x80), PEDERSEN_GSIGMA_Y_1) + mstore(add(f, 0xa0), PEDERSEN_GSIGMA_Y_0) calldatacopy(add(f, 0xc0), commitmentPok, 0x40) - mstore(add(f, 0x100), PEDERSEN_GROOTSIGMANEG_X_1) - mstore(add(f, 0x120), PEDERSEN_GROOTSIGMANEG_X_0) - mstore(add(f, 0x140), PEDERSEN_GROOTSIGMANEG_Y_1) - mstore(add(f, 0x160), PEDERSEN_GROOTSIGMANEG_Y_0) + mstore(add(f, 0x100), PEDERSEN_G_X_1) + mstore(add(f, 0x120), PEDERSEN_G_X_0) + mstore(add(f, 0x140), PEDERSEN_G_Y_1) + mstore(add(f, 0x160), PEDERSEN_G_Y_0) success := staticcall(gas(), PRECOMPILE_VERIFY, f, 0x180, f, 0x20) success := and(success, mload(f)) diff --git a/backend/groth16/bn254/verify.go b/backend/groth16/bn254/verify.go index ab9e4c132..bc9f1bcc5 100644 --- a/backend/groth16/bn254/verify.go +++ b/backend/groth16/bn254/verify.go @@ -104,14 +104,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bw6-633/commitment_test.go b/backend/groth16/bw6-633/commitment_test.go index a832752ff..fa5eaf36b 100644 --- a/backend/groth16/bw6-633/commitment_test.go +++ b/backend/groth16/bw6-633/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bw6-633/marshal.go b/backend/groth16/bw6-633/marshal.go index a801462c9..d0d6ff380 100644 --- a/backend/groth16/bw6-633/marshal.go +++ b/backend/groth16/bw6-633/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bw6-633/marshal_test.go b/backend/groth16/bw6-633/marshal_test.go index 2ff195dc2..53fa6e857 100644 --- a/backend/groth16/bw6-633/marshal_test.go +++ b/backend/groth16/bw6-633/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bw6-633/prove.go b/backend/groth16/bw6-633/prove.go index 55be59061..a4a6153c4 100644 --- a/backend/groth16/bw6-633/prove.go +++ b/backend/groth16/bw6-633/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-633/fr" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bw6-633/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bw6-633/setup.go b/backend/groth16/bw6-633/setup.go index 466755b84..d26cd7c32 100644 --- a/backend/groth16/bw6-633/setup.go +++ b/backend/groth16/bw6-633/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bw6-633/verify.go b/backend/groth16/bw6-633/verify.go index fb4b6dbb2..79ce6b029 100644 --- a/backend/groth16/bw6-633/verify.go +++ b/backend/groth16/bw6-633/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/backend/groth16/bw6-761/commitment_test.go b/backend/groth16/bw6-761/commitment_test.go index f16cb2578..323b82f09 100644 --- a/backend/groth16/bw6-761/commitment_test.go +++ b/backend/groth16/bw6-761/commitment_test.go @@ -148,3 +148,34 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { Two: 2, }) } + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) +} diff --git a/backend/groth16/bw6-761/marshal.go b/backend/groth16/bw6-761/marshal.go index b89105e28..a5766ae5b 100644 --- a/backend/groth16/bw6-761/marshal.go +++ b/backend/groth16/bw6-761/marshal.go @@ -95,24 +95,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -126,40 +116,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -168,66 +162,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } + } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } + // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - return dec.BytesRead(), nil + return n + dec.BytesRead(), nil } // WriteTo writes binary encoding of the key elements to writer @@ -296,7 +296,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } diff --git a/backend/groth16/bw6-761/marshal_test.go b/backend/groth16/bw6-761/marshal_test.go index 3be88f02a..ab8fbc866 100644 --- a/backend/groth16/bw6-761/marshal_test.go +++ b/backend/groth16/bw6-761/marshal_test.go @@ -98,6 +98,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -107,9 +108,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/backend/groth16/bw6-761/prove.go b/backend/groth16/bw6-761/prove.go index f98967d4b..53de1f477 100644 --- a/backend/groth16/bw6-761/prove.go +++ b/backend/groth16/bw6-761/prove.go @@ -23,7 +23,6 @@ import ( "github.com/consensys/gnark-crypto/ecc/bw6-761/fr" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/fft" "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/hash_to_field" - "github.com/consensys/gnark-crypto/ecc/bw6-761/fr/pedersen" "github.com/consensys/gnark/backend" "github.com/consensys/gnark/backend/groth16/internal" "github.com/consensys/gnark/backend/witness" @@ -118,7 +117,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -127,7 +134,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/backend/groth16/bw6-761/setup.go b/backend/groth16/bw6-761/setup.go index 8f7cfc1f4..0988613d3 100644 --- a/backend/groth16/bw6-761/setup.go +++ b/backend/groth16/bw6-761/setup.go @@ -77,7 +77,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -287,10 +287,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/backend/groth16/bw6-761/verify.go b/backend/groth16/bw6-761/verify.go index 718578e64..a00e52d69 100644 --- a/backend/groth16/bw6-761/verify.go +++ b/backend/groth16/bw6-761/verify.go @@ -98,14 +98,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/go.mod b/go.mod index 34a210008..4ed242d67 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/consensys/bavard v0.1.13 github.com/consensys/compress v0.2.5 - github.com/consensys/gnark-crypto v0.13.1-0.20240802214859-ff4c0ddbe1ef + github.com/consensys/gnark-crypto v0.14.0 github.com/fxamacker/cbor/v2 v2.7.0 github.com/google/go-cmp v0.6.0 github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 diff --git a/go.sum b/go.sum index 72842c60a..efc71ddf9 100644 --- a/go.sum +++ b/go.sum @@ -61,8 +61,8 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/compress v0.2.5 h1:gJr1hKzbOD36JFsF1AN8lfXz1yevnJi1YolffY19Ntk= github.com/consensys/compress v0.2.5/go.mod h1:pyM+ZXiNUh7/0+AUjUf9RKUM6vSH7T/fsn5LLS0j1Tk= -github.com/consensys/gnark-crypto v0.13.1-0.20240802214859-ff4c0ddbe1ef h1:4DaS1IYXk0vKcCdguGjkHVyN43YqmKUmpYDxb90VBnU= -github.com/consensys/gnark-crypto v0.13.1-0.20240802214859-ff4c0ddbe1ef/go.mod h1:wKqwsieaKPThcFkHe0d0zMsbHEUWFmZcG7KBCse210o= +github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E= +github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= diff --git a/internal/generator/backend/template/zkpschemes/groth16/groth16.marshal.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/groth16.marshal.go.tmpl index 4199f6cf4..5f11ac043 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/groth16.marshal.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/groth16.marshal.go.tmpl @@ -77,24 +77,14 @@ func (proof *Proof) ReadFrom(r io.Reader) (n int64, err error) { // points are compressed // use WriteRawTo(...) to encode the key without point compression func (vk *VerifyingKey) WriteTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, false); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteTo(w) - return m + n, err + return vk.writeTo(w, false) } // WriteRawTo writes binary encoding of the key elements to writer // points are not compressed // use WriteTo(...) to encode the key with point compression func (vk *VerifyingKey) WriteRawTo(w io.Writer) (n int64, err error) { - if n, err = vk.writeTo(w, true); err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.WriteRawTo(w) - return m + n, err + return vk.writeTo(w, true) } // writeTo serialization format: @@ -108,41 +98,44 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { } else { enc = curve.NewEncoder(w) } - - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := enc.Encode(&vk.G1.Alpha); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Beta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Gamma); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G1.Delta); err != nil { - return enc.BytesWritten(), err - } - if err := enc.Encode(&vk.G2.Delta); err != nil { - return enc.BytesWritten(), err - } - - // uint32(len(Kvk)),[Kvk]1 - if err := enc.Encode(vk.G1.K); err != nil { - return enc.BytesWritten(), err - } - if vk.PublicAndCommitmentCommitted == nil { vk.PublicAndCommitmentCommitted = [][]int{} // only matters in tests } - if err := enc.Encode(utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted)); err != nil { - return enc.BytesWritten(), err + toEncode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + vk.G1.K, + utils.IntSliceSliceToUint64SliceSlice(vk.PublicAndCommitmentCommitted), + uint32(len(vk.CommitmentKeys)), } - - return enc.BytesWritten(), nil + for _, v := range toEncode { + if err := enc.Encode(v); err != nil { + return enc.BytesWritten(), err + } + } + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].WriteRawTo(w) + } else { + m, err = vk.CommitmentKeys[i].WriteTo(w) + } + n += m + if err != nil { + return n + enc.BytesWritten(), err + } + } + return n + enc.BytesWritten(), nil } // ReadFrom attempts to decode a VerifyingKey from reader @@ -151,66 +144,72 @@ func (vk *VerifyingKey) writeTo(w io.Writer, raw bool) (int64, error) { // https://github.com/zkcrypto/bellman/blob/fa9be45588227a8c6ec34957de3f68705f07bd92/src/groth16/mod.rs#L143 // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2,uint32(len(Kvk)),[Kvk]1 func (vk *VerifyingKey) ReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.ReadFrom(r) - return m + n, err + return vk.readFrom(r, false) } // UnsafeReadFrom has the same behavior as ReadFrom, except that it will not check that decode points // are on the curve and in the correct subgroup. func (vk *VerifyingKey) UnsafeReadFrom(r io.Reader) (int64, error) { - n, err := vk.readFrom(r, curve.NoSubgroupChecks()) - if err != nil { - return n, err - } - var m int64 - m, err = vk.CommitmentKey.UnsafeReadFrom(r) - return m + n, err + return vk.readFrom(r, true) } -func (vk *VerifyingKey) readFrom(r io.Reader, decOptions ...func(*curve.Decoder)) (int64, error) { - dec := curve.NewDecoder(r, decOptions...) - - // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 - if err := dec.Decode(&vk.G1.Alpha); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Beta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Gamma); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G1.Delta); err != nil { - return dec.BytesRead(), err - } - if err := dec.Decode(&vk.G2.Delta); err != nil { - return dec.BytesRead(), err +func (vk *VerifyingKey) readFrom(r io.Reader, raw bool) (int64, error) { + var dec *curve.Decoder + if raw { + dec = curve.NewDecoder(r, curve.NoSubgroupChecks()) + } else { + dec = curve.NewDecoder(r) } - // uint32(len(Kvk)),[Kvk]1 - if err := dec.Decode(&vk.G1.K); err != nil { - return dec.BytesRead(), err - } var publicCommitted [][]uint64 - if err := dec.Decode(&publicCommitted); err != nil { - return dec.BytesRead(), err + var nbCommitments uint32 + + toDecode := []interface{}{ + // [α]1,[β]1,[β]2,[γ]2,[δ]1,[δ]2 + &vk.G1.Alpha, + &vk.G1.Beta, + &vk.G2.Beta, + &vk.G2.Gamma, + &vk.G1.Delta, + &vk.G2.Delta, + // uint32(len(Kvk)),[Kvk]1 + &vk.G1.K, + &publicCommitted, + &nbCommitments, + } + + for _, v := range toDecode { + if err := dec.Decode(v); err != nil { + return dec.BytesRead(), err + } } + vk.PublicAndCommitmentCommitted = utils.Uint64SliceSliceToIntSliceSlice(publicCommitted) + + vk.CommitmentKeys = make([]pedersen.VerifyingKey, nbCommitments) + var n int64 + for i := range vk.CommitmentKeys { + var ( + m int64 + err error + ) + if raw { + m, err = vk.CommitmentKeys[i].UnsafeReadFrom(r) + } else { + m, err = vk.CommitmentKeys[i].ReadFrom(r) + } + n += m + if err != nil { + return n + dec.BytesRead(), err + } + } // recompute vk.e (e(α, β)) and -[δ]2, -[γ]2 if err := vk.Precompute(); err != nil { - return dec.BytesRead(), err + return n + dec.BytesRead(), err } - - return dec.BytesRead(), nil + + return n + dec.BytesRead(), nil } @@ -282,7 +281,7 @@ func (pk *ProvingKey) writeTo(w io.Writer, raw bool) (int64, error) { n += n2 if err != nil { - return n, err + return n + enc.BytesWritten(), err } } 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 4bc05dd14..b94532519 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/groth16.prove.go.tmpl @@ -9,7 +9,6 @@ import ( {{- template "import_backend_cs" . }} {{- template "import_fft" . }} {{- template "import_hash_to_field" . }} - {{- template "import_pedersen" .}} "github.com/consensys/gnark/constraint" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark/internal/utils" @@ -102,7 +101,15 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b wireValues := []fr.Element(solution.W) start := time.Now() + poks := make([]curve.G1Affine, len(pk.CommitmentKeys)) + for i := range pk.CommitmentKeys { + var err error + if poks[i], err = pk.CommitmentKeys[i].ProveKnowledge(privateCommittedValues[i]); err != nil { + return nil, err + } + } + // compute challenge for folding the PoKs from the commitments commitmentsSerialized := make([]byte, fr.Bytes*len(commitmentInfo)) for i := range commitmentInfo { copy(commitmentsSerialized[fr.Bytes*i:], wireValues[commitmentInfo[i].CommitmentIndex].Marshal()) @@ -111,7 +118,7 @@ func Prove(r1cs *cs.R1CS, pk *ProvingKey, fullWitness witness.Witness, opts ...b if err != nil { return nil, err } - if proof.CommitmentPok, err = pedersen.BatchProve(pk.CommitmentKeys, privateCommittedValues, challenge[0]); err != nil { + if _, err = proof.CommitmentPok.Fold(poks, challenge[0], ecc.MultiExpConfig{NbTasks: 1}); err != nil { return nil, err } diff --git a/internal/generator/backend/template/zkpschemes/groth16/groth16.setup.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/groth16.setup.go.tmpl index 81af9215b..ae86a7c39 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/groth16.setup.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/groth16.setup.go.tmpl @@ -59,7 +59,7 @@ type VerifyingKey struct { // e(α, β) e curve.GT // not serialized - CommitmentKey pedersen.VerifyingKey + CommitmentKeys []pedersen.VerifyingKey PublicAndCommitmentCommitted [][]int // indexes of public/commitment committed variables } @@ -269,10 +269,20 @@ func Setup(r1cs *cs.R1CS, pk *ProvingKey, vk *VerifyingKey) error { return errors.New("didn't consume all G1 points") // TODO @Tabaie Remove this } - pk.CommitmentKeys, vk.CommitmentKey, err = pedersen.Setup(commitmentBases) + cG2, err := curve.RandomOnG2() if err != nil { return err } + pk.CommitmentKeys = make([]pedersen.ProvingKey, len(commitmentBases)) + vk.CommitmentKeys = make([]pedersen.VerifyingKey, len(commitmentBases)) + for i := range commitmentBases { + comPKey, comVKey, err := pedersen.Setup(commitmentBases[i:i+1], pedersen.WithG2Point(cG2)) + if err != nil { + return err + } + pk.CommitmentKeys[i] = comPKey[0] + vk.CommitmentKeys[i] = comVKey + } vk.PublicAndCommitmentCommitted = commitmentInfo.GetPublicAndCommitmentCommitted(commitmentWires, r1cs.GetNbPublicVariables()) diff --git a/internal/generator/backend/template/zkpschemes/groth16/groth16.verify.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/groth16.verify.go.tmpl index b64fe618d..97e953a7d 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/groth16.verify.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/groth16.verify.go.tmpl @@ -88,14 +88,12 @@ func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...bac publicWitness = append(publicWitness, res) copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) } - challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) - if err != nil { - return err - } - if folded, err := pedersen.FoldCommitments(proof.Commitments, challenge[0]); err != nil { - return err - } else { - if err = vk.CommitmentKey.Verify(folded, proof.CommitmentPok); err != nil { + if len(vk.CommitmentKeys) > 0 { + challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) + if err != nil { + return err + } + if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { return err } } diff --git a/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.commitment.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.commitment.go.tmpl index cc94c21ef..556754e3e 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.commitment.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.commitment.go.tmpl @@ -129,4 +129,35 @@ func TestOneSecretOnePublicCommitted(t *testing.T) { One: 1, Two: 2, }) +} + +type twoSecretCommitmentsCircuit struct { + One frontend.Variable + Two frontend.Variable +} + +func (c *twoSecretCommitmentsCircuit) Define(api frontend.API) error { + commitCompiler, ok := api.Compiler().(frontend.Committer) + if !ok { + return fmt.Errorf("compiler does not commit") + } + commit1, err := commitCompiler.Commit(c.One) + if err != nil { + return err + } + commit2, err := commitCompiler.Commit(c.Two) + if err != nil { + return err + } + // constrain vars + api.AssertIsDifferent(commit1, 0) + api.AssertIsDifferent(commit2, 0) + return nil +} + +func TestTwoSecretCommitments(t *testing.T) { + test(t, &twoSecretCommitmentsCircuit{}, &twoSecretCommitmentsCircuit{ + One: 1, + Two: 2, + }) } \ No newline at end of file diff --git a/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.marshal.go.tmpl b/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.marshal.go.tmpl index 7df6fa02b..699704358 100644 --- a/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.marshal.go.tmpl +++ b/internal/generator/backend/template/zkpschemes/groth16/tests/groth16.marshal.go.tmpl @@ -82,6 +82,7 @@ func TestVerifyingKeySerialization(t *testing.T) { vk.G1.K[i] = p1 } + vk.CommitmentKeys = []pedersen.VerifyingKey{} if withCommitment { vk.PublicAndCommitmentCommitted = test_utils.Random2DIntSlice(5, 10) // TODO: Use gopter randomization bases := make([][]curve.G1Affine, len(vk.PublicAndCommitmentCommitted)) @@ -91,9 +92,9 @@ func TestVerifyingKeySerialization(t *testing.T) { for j := range bases[i] { bases[i][j] = elem elem.Add(&elem, &p1) + vk.CommitmentKeys = append(vk.CommitmentKeys, pedersen.VerifyingKey{G: p2, GSigma: p2}) } } - _, vk.CommitmentKey, err = pedersen.Setup(bases) assert.NoError(t, err) } diff --git a/std/commitments/pedersen/assignment.go b/std/commitments/pedersen/assignment.go index bb90d89f5..8822c5818 100644 --- a/std/commitments/pedersen/assignment.go +++ b/std/commitments/pedersen/assignment.go @@ -34,35 +34,35 @@ func ValueOfVerifyingKey[G2El algebra.G2ElementT](vk any) (VerifyingKey[G2El], e return ret, fmt.Errorf("expected *ped_bls12377.VerifyingKey, got %T", vk) } s.G = sw_bls12377.NewG2Affine(tVk.G) - s.GRootSigmaNeg = sw_bls12377.NewG2Affine(tVk.GRootSigmaNeg) + s.GSigma = sw_bls12377.NewG2Affine(tVk.GSigma) case *VerifyingKey[sw_bls12381.G2Affine]: tVk, ok := vk.(*ped_bls12381.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bls12381.VerifyingKey, got %T", vk) } s.G = sw_bls12381.NewG2Affine(tVk.G) - s.GRootSigmaNeg = sw_bls12381.NewG2Affine(tVk.GRootSigmaNeg) + s.GSigma = sw_bls12381.NewG2Affine(tVk.GSigma) case *VerifyingKey[sw_bls24315.G2Affine]: tVk, ok := vk.(*ped_bls24315.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bls24315.VerifyingKey, got %T", vk) } s.G = sw_bls24315.NewG2Affine(tVk.G) - s.GRootSigmaNeg = sw_bls24315.NewG2Affine(tVk.GRootSigmaNeg) + s.GSigma = sw_bls24315.NewG2Affine(tVk.GSigma) case *VerifyingKey[sw_bw6761.G2Affine]: tVk, ok := vk.(*ped_bw6761.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bw6761.VerifyingKey, got %T", vk) } s.G = sw_bw6761.NewG2Affine(tVk.G) - s.GRootSigmaNeg = sw_bw6761.NewG2Affine(tVk.GRootSigmaNeg) + s.GSigma = sw_bw6761.NewG2Affine(tVk.GSigma) case *VerifyingKey[sw_bn254.G2Affine]: tVk, ok := vk.(*ped_bn254.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bn254.VerifyingKey, got %T", vk) } s.G = sw_bn254.NewG2Affine(tVk.G) - s.GRootSigmaNeg = sw_bn254.NewG2Affine(tVk.GRootSigmaNeg) + s.GSigma = sw_bn254.NewG2Affine(tVk.GSigma) default: panic(fmt.Sprintf("unknown parametric type: %T", s)) } @@ -82,35 +82,35 @@ func ValueOfVerifyingKeyFixed[G2El algebra.G2ElementT](vk any) (VerifyingKey[G2E return ret, fmt.Errorf("expected *ped_bls12377.VerifyingKey, got %T", vk) } s.G = sw_bls12377.NewG2AffineFixed(tVk.G) - s.GRootSigmaNeg = sw_bls12377.NewG2AffineFixed(tVk.GRootSigmaNeg) + s.GSigma = sw_bls12377.NewG2AffineFixed(tVk.GSigma) case *VerifyingKey[sw_bls12381.G2Affine]: tVk, ok := vk.(*ped_bls12381.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bls12381.VerifyingKey, got %T", vk) } s.G = sw_bls12381.NewG2AffineFixed(tVk.G) - s.GRootSigmaNeg = sw_bls12381.NewG2AffineFixed(tVk.GRootSigmaNeg) + s.GSigma = sw_bls12381.NewG2AffineFixed(tVk.GSigma) case *VerifyingKey[sw_bls24315.G2Affine]: tVk, ok := vk.(*ped_bls24315.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bls24315.VerifyingKey, got %T", vk) } s.G = sw_bls24315.NewG2AffineFixed(tVk.G) - s.GRootSigmaNeg = sw_bls24315.NewG2AffineFixed(tVk.GRootSigmaNeg) + s.GSigma = sw_bls24315.NewG2AffineFixed(tVk.GSigma) case *VerifyingKey[sw_bw6761.G2Affine]: tVk, ok := vk.(*ped_bw6761.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bw6761.VerifyingKey, got %T", vk) } s.G = sw_bw6761.NewG2AffineFixed(tVk.G) - s.GRootSigmaNeg = sw_bw6761.NewG2AffineFixed(tVk.GRootSigmaNeg) + s.GSigma = sw_bw6761.NewG2AffineFixed(tVk.GSigma) case *VerifyingKey[sw_bn254.G2Affine]: tVk, ok := vk.(*ped_bn254.VerifyingKey) if !ok { return ret, fmt.Errorf("expected *ped_bn254.VerifyingKey, got %T", vk) } s.G = sw_bn254.NewG2AffineFixed(tVk.G) - s.GRootSigmaNeg = sw_bn254.NewG2AffineFixed(tVk.GRootSigmaNeg) + s.GSigma = sw_bn254.NewG2AffineFixed(tVk.GSigma) default: return ret, fmt.Errorf("unknown parametric type: %T", s) } diff --git a/std/commitments/pedersen/verifier.go b/std/commitments/pedersen/verifier.go index 136ad0a6b..b1a9afed0 100644 --- a/std/commitments/pedersen/verifier.go +++ b/std/commitments/pedersen/verifier.go @@ -21,8 +21,8 @@ type KnowledgeProof[G1El algebra.G1ElementT] struct { // VerifyingKey is a verifying key for Pedersen vector commitments. type VerifyingKey[G2El algebra.G2ElementT] struct { - G G2El - GRootSigmaNeg G2El // (-1/σ)[G] for toxic σ + G G2El + GSigma G2El // (-1/σ)[G] for toxic σ } // Verifier verifies the knowledge proofs for a Pedersen commitments @@ -63,7 +63,9 @@ func (v *Verifier[FR, G1El, G2El, GtEl]) AssertCommitment(commitment Commitment[ v.pairing.AssertIsOnG1(&knowledgeProof.G1El) } - v.pairing.PairingCheck([]*G1El{&commitment.G1El, &knowledgeProof.G1El}, []*G2El{&vk.G, &vk.GRootSigmaNeg}) + if err = v.pairing.PairingCheck([]*G1El{&commitment.G1El, &knowledgeProof.G1El}, []*G2El{&vk.GSigma, &vk.G}); err != nil { + return fmt.Errorf("pairing check failed: %w", err) + } return nil } diff --git a/std/recursion/groth16/verifier.go b/std/recursion/groth16/verifier.go index dfa452174..ad760db91 100644 --- a/std/recursion/groth16/verifier.go +++ b/std/recursion/groth16/verifier.go @@ -166,7 +166,7 @@ type VerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl algebra E GtEl G1 struct{ K []G1El } G2 struct{ GammaNeg, DeltaNeg G2El } - CommitmentKey pedersen.VerifyingKey[G2El] + CommitmentKeys []pedersen.VerifyingKey[G2El] PublicAndCommitmentCommitted [][]int } @@ -183,6 +183,7 @@ func PlaceholderVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, G K: make([]G1El, ccs.GetNbPublicVariables()+len(ccs.GetCommitments().(constraint.Groth16Commitments))), }, PublicAndCommitmentCommitted: commitments.GetPublicAndCommitmentCommitted(commitmentWires, ccs.GetNbPublicVariables()), + CommitmentKeys: make([]pedersen.VerifyingKey[G2El], len(commitments)), } } @@ -212,9 +213,12 @@ func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bn254.NewG2Affine(deltaNeg) s.G2.GammaNeg = sw_bn254.NewG2Affine(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKey[sw_bn254.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bn254.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKey[sw_bn254.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]: tVk, ok := vk.(*groth16backend_bls12377.VerifyingKey) @@ -236,9 +240,12 @@ func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls12377.NewG2Affine(deltaNeg) s.G2.GammaNeg = sw_bls12377.NewG2Affine(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKey[sw_bls12377.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls12377.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKey[sw_bls12377.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls12381.G1Affine, sw_bls12381.G2Affine, sw_bls12381.GTEl]: tVk, ok := vk.(*groth16backend_bls12381.VerifyingKey) @@ -260,9 +267,12 @@ func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls12381.NewG2Affine(deltaNeg) s.G2.GammaNeg = sw_bls12381.NewG2Affine(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKey[sw_bls12381.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls12381.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKey[sw_bls12381.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls24315.G1Affine, sw_bls24315.G2Affine, sw_bls24315.GT]: tVk, ok := vk.(*groth16backend_bls24315.VerifyingKey) @@ -284,9 +294,12 @@ func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls24315.NewG2Affine(deltaNeg) s.G2.GammaNeg = sw_bls24315.NewG2Affine(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKey[sw_bls24315.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls24315.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKey[sw_bls24315.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]: tVk, ok := vk.(*groth16backend_bw6761.VerifyingKey) @@ -308,9 +321,12 @@ func ValueOfVerifyingKey[G1El algebra.G1ElementT, G2El algebra.G2ElementT, GtEl gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bw6761.NewG2Affine(deltaNeg) s.G2.GammaNeg = sw_bw6761.NewG2Affine(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKey[sw_bw6761.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bw6761.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKey[sw_bw6761.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } default: return ret, fmt.Errorf("unknown parametric type combination") @@ -344,9 +360,12 @@ func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bn254.NewG2AffineFixed(deltaNeg) s.G2.GammaNeg = sw_bn254.NewG2AffineFixed(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKeyFixed[sw_bn254.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bn254.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKeyFixed[sw_bn254.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls12377.G1Affine, sw_bls12377.G2Affine, sw_bls12377.GT]: tVk, ok := vk.(*groth16backend_bls12377.VerifyingKey) @@ -368,9 +387,12 @@ func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls12377.NewG2AffineFixed(deltaNeg) s.G2.GammaNeg = sw_bls12377.NewG2AffineFixed(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKeyFixed[sw_bls12377.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls12377.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKeyFixed[sw_bls12377.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls12381.G1Affine, sw_bls12381.G2Affine, sw_bls12381.GTEl]: tVk, ok := vk.(*groth16backend_bls12381.VerifyingKey) @@ -392,9 +414,12 @@ func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls12381.NewG2AffineFixed(deltaNeg) s.G2.GammaNeg = sw_bls12381.NewG2AffineFixed(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKeyFixed[sw_bls12381.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls12381.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKeyFixed[sw_bls12381.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bls24315.G1Affine, sw_bls24315.G2Affine, sw_bls24315.GT]: tVk, ok := vk.(*groth16backend_bls24315.VerifyingKey) @@ -416,9 +441,12 @@ func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bls24315.NewG2AffineFixed(deltaNeg) s.G2.GammaNeg = sw_bls24315.NewG2AffineFixed(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKeyFixed[sw_bls24315.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bls24315.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKeyFixed[sw_bls24315.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } case *VerifyingKey[sw_bw6761.G1Affine, sw_bw6761.G2Affine, sw_bw6761.GTEl]: tVk, ok := vk.(*groth16backend_bw6761.VerifyingKey) @@ -440,9 +468,12 @@ func ValueOfVerifyingKeyFixed[G1El algebra.G1ElementT, G2El algebra.G2ElementT, gammaNeg.Neg(&tVk.G2.Gamma) s.G2.DeltaNeg = sw_bw6761.NewG2AffineFixed(deltaNeg) s.G2.GammaNeg = sw_bw6761.NewG2AffineFixed(gammaNeg) - s.CommitmentKey, err = pedersen.ValueOfVerifyingKeyFixed[sw_bw6761.G2Affine](&tVk.CommitmentKey) - if err != nil { - return ret, fmt.Errorf("commitment key: %w", err) + s.CommitmentKeys = make([]pedersen.VerifyingKey[sw_bw6761.G2Affine], len(tVk.CommitmentKeys)) + for i := range tVk.CommitmentKeys { + s.CommitmentKeys[i], err = pedersen.ValueOfVerifyingKeyFixed[sw_bw6761.G2Affine](&tVk.CommitmentKeys[i]) + if err != nil { + return ret, fmt.Errorf("commitment key[%d]: %w", i, err) + } } default: return ret, fmt.Errorf("unknown parametric type combination") @@ -567,6 +598,12 @@ func NewVerifier[FR emulated.FieldParams, G1El algebra.G1ElementT, G2El algebra. // AssertProof asserts that the SNARK proof holds for the given witness and // verifying key. func (v *Verifier[FR, G1El, G2El, GtEl]) AssertProof(vk VerifyingKey[G1El, G2El, GtEl], proof Proof[G1El, G2El], witness Witness[FR], opts ...VerifierOption) error { + if len(vk.CommitmentKeys) != len(proof.Commitments) { + return fmt.Errorf("invalid number of commitments, got %d, expected %d", len(proof.Commitments), len(vk.CommitmentKeys)) + } + if len(vk.CommitmentKeys) != len(vk.PublicAndCommitmentCommitted) { + return fmt.Errorf("invalid number of commitment keys, got %d, expected %d", len(vk.CommitmentKeys), len(vk.PublicAndCommitmentCommitted)) + } var fr FR nbPublicVars := len(vk.G1.K) - len(vk.PublicAndCommitmentCommitted) if len(witness.Public) != nbPublicVars-1 { @@ -612,15 +649,16 @@ func (v *Verifier[FR, G1El, G2El, GtEl]) AssertProof(vk VerifyingKey[G1El, G2El, commitmentAuxData[i] = res } - if len(vk.PublicAndCommitmentCommitted) > 0 { - folded, err := v.commitment.FoldCommitments(proof.Commitments, commitmentAuxData...) - if err != nil { - return fmt.Errorf("fold commitments: %w", err) - } - err = v.commitment.AssertCommitment(folded, proof.CommitmentPok, vk.CommitmentKey, opt.pedopt...) - if err != nil { + switch len(vk.CommitmentKeys) { + case 0: + // explicitly do not verify the commitment as there is nothing + case 1: + if err = v.commitment.AssertCommitment(proof.Commitments[0], proof.CommitmentPok, vk.CommitmentKeys[0], opt.pedopt...); err != nil { return fmt.Errorf("assert commitment: %w", err) } + default: + // TODO: we support only a single commitment in the recursion for now + return fmt.Errorf("multiple commitments are not supported") } kSum, err := v.curve.MultiScalarMul(inP, inS, opt.algopt...) diff --git a/test/assert_checkcircuit.go b/test/assert_checkcircuit.go index c631623d7..15789da5f 100644 --- a/test/assert_checkcircuit.go +++ b/test/assert_checkcircuit.go @@ -10,6 +10,7 @@ import ( "github.com/consensys/gnark/constraint" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/schema" + "github.com/consensys/gnark/logger" "github.com/consensys/gnark/test/unsafekzg" ) @@ -31,6 +32,7 @@ import ( func (assert *Assert) CheckCircuit(circuit frontend.Circuit, opts ...TestingOption) { // get the testing configuration opt := assert.options(opts...) + log := logger.Logger() // for each {curve, backend} tuple for _, curve := range opt.curves { @@ -125,6 +127,11 @@ func (assert *Assert) CheckCircuit(circuit frontend.Circuit, opts ...TestingOpti verifierOpts := opt.verifierOpts if b == backend.GROTH16 { // currently groth16 Solidity checker only supports circuits with up to 1 commitment + if len(ccs.GetCommitments().CommitmentIndexes()) > 1 { + log.Warn(). + Int("nb_commitments", len(ccs.GetCommitments().CommitmentIndexes())). + Msg("skipping solidity check, too many commitments") + } checkSolidity = checkSolidity && (len(ccs.GetCommitments().CommitmentIndexes()) <= 1) // set the default hash function in case of custom hash function not set. This is to ensure that the proof can be verified by gnark-solidity-checker proverOpts = append([]backend.ProverOption{solidity.WithProverTargetSolidityVerifier(b)}, opt.proverOpts...)