diff --git a/ecc/bn254/kzg/icicle.go b/ecc/bn254/kzg/icicle.go new file mode 100644 index 0000000000..54ca9b6954 --- /dev/null +++ b/ecc/bn254/kzg/icicle.go @@ -0,0 +1,200 @@ +package kzg + +import ( + "fmt" + "hash" + "sync" + "unsafe" + + "github.com/consensys/gnark-crypto/ecc/bn254" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/internal/parallel" + iciclegnark "github.com/ingonyama-zk/iciclegnark/curves/bn254" +) + +// Commit commits to a polynomial using a multi exponentiation with the SRS. +// It is assumed that the polynomial is in canonical form, in Montgomery form. +func OnDeviceCommit(p []fr.Element, G1 unsafe.Pointer, nbTasks ...int) (Digest, error) { + // Size of the polynomial + np := len(p) + + // Size of the polynomial in bytes + sizeBytesScalars := np * fr.Bytes + + // Initialize Scalar channels + copyCpDone := make(chan unsafe.Pointer, 1) + cpDeviceData := make(chan iciclegnark.OnDeviceData, 1) + + // Copy Scalar to device + go func() { + // Perform copy operation + iciclegnark.CopyToDevice(p, sizeBytesScalars, copyCpDone) + + // Receive result once copy operation is done + cpDevice := <-copyCpDone + + // Create OnDeviceData + cpDeviceValue := iciclegnark.OnDeviceData{ + P: cpDevice, + Size: sizeBytesScalars, + } + + // Send OnDeviceData to respective channel + cpDeviceData <- cpDeviceValue + + // Close channels + close(copyCpDone) + close(cpDeviceData) + }() + + // Wait for copy operation to finish + cpDeviceValue := <-cpDeviceData + + // KZG Committment on device + var wg sync.WaitGroup + + // Perform multi exponentiation on device + wg.Add(1) + tmpChan := make(chan bn254.G1Affine, 1) + go func() { + defer wg.Done() + tmp, _, err := iciclegnark.MsmOnDevice(cpDeviceValue.P, G1, np, true) + //fmt.Println("tmp", tmp) + if err != nil { + fmt.Print("error", err) + } + var res bn254.G1Affine + res.FromJacobian(&tmp) + tmpChan <- res + }() + wg.Wait() + + // Receive result once copy operation is done + res := <-tmpChan + + // Free device memory + go func() { + iciclegnark.FreeDevicePointer(unsafe.Pointer(&cpDeviceValue)) + }() + + return res, nil +} + +// Open computes an opening proof of polynomial p at given point. +// fft.Domain Cardinality must be larger than p.Degree() +func OnDeviceOpen(p []fr.Element, point fr.Element, G1 unsafe.Pointer) (OpeningProof, error) { + // build the proof + res := OpeningProof{ + ClaimedValue: eval(p, point), + } + + // compute H + // h reuses memory from _p + _p := make([]fr.Element, len(p)) + copy(_p, p) + h := dividePolyByXminusA(_p, res.ClaimedValue, point) + + // commit to H + hCommit, err := OnDeviceCommit(h, G1) + if err != nil { + return OpeningProof{}, err + } + res.H.Set(&hCommit) + + return res, nil +} + +// BatchOpenSinglePoint creates a batch opening proof at point of a list of polynomials. +// It's an interactive protocol, made non-interactive using Fiat Shamir. +// +// * point is the point at which the polynomials are opened. +// * digests is the list of committed polynomials to open, need to derive the challenge using Fiat Shamir. +// * polynomials is the list of polynomials to open, they are supposed to be of the same size. +// * dataTranscript extra data that might be needed to derive the challenge used for folding +func OnDeviceBatchOpenSinglePoint(polynomials [][]fr.Element, digests []Digest, point fr.Element, hf hash.Hash, pk ProvingKey, G1 unsafe.Pointer, dataTranscript ...[]byte) (BatchOpeningProof, error) { + + // check for invalid sizes + nbDigests := len(digests) + if nbDigests != len(polynomials) { + return BatchOpeningProof{}, ErrInvalidNbDigests + } + + // TODO ensure the polynomials are of the same size + largestPoly := -1 + for _, p := range polynomials { + if len(p) == 0 || len(p) > len(pk.G1) { + return BatchOpeningProof{}, ErrInvalidPolynomialSize + } + if len(p) > largestPoly { + largestPoly = len(p) + } + } + + var res BatchOpeningProof + + // compute the purported values + res.ClaimedValues = make([]fr.Element, len(polynomials)) + var wg sync.WaitGroup + wg.Add(len(polynomials)) + for i := 0; i < len(polynomials); i++ { + go func(_i int) { + res.ClaimedValues[_i] = eval(polynomials[_i], point) + wg.Done() + }(i) + } + + // wait for polynomial evaluations to be completed (res.ClaimedValues) + wg.Wait() + + // derive the challenge γ, binded to the point and the commitments + gamma, err := deriveGamma(point, digests, res.ClaimedValues, hf, dataTranscript...) + if err != nil { + return BatchOpeningProof{}, err + } + + // ∑ᵢγⁱf(a) + var foldedEvaluations fr.Element + chSumGammai := make(chan struct{}, 1) + go func() { + foldedEvaluations = res.ClaimedValues[nbDigests-1] + for i := nbDigests - 2; i >= 0; i-- { + foldedEvaluations.Mul(&foldedEvaluations, &gamma). + Add(&foldedEvaluations, &res.ClaimedValues[i]) + } + close(chSumGammai) + }() + + // compute ∑ᵢγⁱfᵢ + // note: if we are willing to parallelize that, we could clone the poly and scale them by + // gamma n in parallel, before reducing into foldedPolynomials + foldedPolynomials := make([]fr.Element, largestPoly) + copy(foldedPolynomials, polynomials[0]) + gammas := make([]fr.Element, len(polynomials)) + gammas[0] = gamma + for i := 1; i < len(polynomials); i++ { + gammas[i].Mul(&gammas[i-1], &gamma) + } + + for i := 1; i < len(polynomials); i++ { + i := i + parallel.Execute(len(polynomials[i]), func(start, end int) { + var pj fr.Element + for j := start; j < end; j++ { + pj.Mul(&polynomials[i][j], &gammas[i-1]) + foldedPolynomials[j].Add(&foldedPolynomials[j], &pj) + } + }) + } + + // compute H + <-chSumGammai + h := dividePolyByXminusA(foldedPolynomials, foldedEvaluations, point) + foldedPolynomials = nil // same memory as h + + res.H, err = OnDeviceCommit(h, G1) + if err != nil { + return BatchOpeningProof{}, err + } + + return res, nil +} diff --git a/ecc/bn254/kzg/kzg.go b/ecc/bn254/kzg/kzg.go index 067894ab5e..c6a536c557 100644 --- a/ecc/bn254/kzg/kzg.go +++ b/ecc/bn254/kzg/kzg.go @@ -28,6 +28,7 @@ import ( "github.com/consensys/gnark-crypto/fiat-shamir" "github.com/consensys/gnark-crypto/internal/parallel" + ) var ( @@ -186,6 +187,7 @@ func Commit(p []fr.Element, pk ProvingKey, nbTasks ...int) (Digest, error) { return res, nil } + // Open computes an opening proof of polynomial p at given point. // fft.Domain Cardinality must be larger than p.Degree() func Open(p []fr.Element, point fr.Element, pk ProvingKey) (OpeningProof, error) { @@ -599,3 +601,4 @@ func dividePolyByXminusA(f []fr.Element, fa, a fr.Element) []fr.Element { // the result is of degree deg(f)-1 return f[1:] } + diff --git a/ecc/bn254/kzg/kzg_test.go b/ecc/bn254/kzg/kzg_test.go index 97adc15d81..cbc9ca70c5 100644 --- a/ecc/bn254/kzg/kzg_test.go +++ b/ecc/bn254/kzg/kzg_test.go @@ -17,6 +17,7 @@ package kzg import ( + "unsafe" "crypto/sha256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,6 +30,9 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/fft" "github.com/consensys/gnark-crypto/utils" + + "github.com/consensys/gnark-crypto/ecc/bn254/fp" + iciclegnark "github.com/ingonyama-zk/iciclegnark/curves/bn254" ) // Test SRS re-used across tests of the KZG scheme @@ -196,6 +200,38 @@ func TestCommit(t *testing.T) { } +func TestOnDeviceCommit(t *testing.T) { + + // create a polynomial + f := make([]fr.Element, 60) + for i := 0; i < 60; i++ { + f[i].SetRandom() + } + + // Initialize Scalar channels + copyG1Done := make(chan unsafe.Pointer, 1) + + pointsBytesG1 := len(testSrs.Pk.G1) * fp.Bytes * 2 + go iciclegnark.CopyPointsToDevice(testSrs.Pk.G1, pointsBytesG1, copyG1Done) // Make a function for points + G1Device := <-copyG1Done + + onDeviceCommit, _ := OnDeviceCommit(f, G1Device, 1) + + // check commitment using manual commit + var x fr.Element + x.SetString("42") + fx := eval(f, x) + var fxbi big.Int + fx.BigInt(&fxbi) + var manualCommit bn254.G1Affine + manualCommit.Set(&testSrs.Vk.G1) + manualCommit.ScalarMultiplication(&manualCommit, &fxbi) + + if !onDeviceCommit.Equal(&manualCommit) { + t.Fatal("error OnDeviceCommit") + } +} + func TestVerifySinglePoint(t *testing.T) { // create a polynomial diff --git a/go.mod b/go.mod index 79d762f085..18ff102803 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,11 @@ go 1.18 require ( github.com/bits-and-blooms/bitset v1.7.0 github.com/consensys/bavard v0.1.13 + github.com/ingonyama-zk/iciclegnark v0.1.0 github.com/leanovate/gopter v0.2.9 github.com/mmcloughlin/addchain v0.4.0 github.com/spf13/cobra v1.5.0 - github.com/stretchr/testify v1.8.2 + github.com/stretchr/testify v1.8.3 golang.org/x/crypto v0.10.0 golang.org/x/sys v0.9.0 gopkg.in/yaml.v2 v2.4.0 @@ -17,6 +18,7 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/ingonyama-zk/icicle v0.1.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect diff --git a/go.sum b/go.sum index a64fd36d8f..496965ff45 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,15 @@ 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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ingonyama-zk/icicle v0.1.0 h1:9zbHaYv8/4g3HWRabBCpeH+64U8GJ99K1qeqE2jO6LM= +github.com/ingonyama-zk/icicle v0.1.0/go.mod h1:kAK8/EoN7fUEmakzgZIYdWy1a2rBnpCaZLqSHwZWxEk= +github.com/ingonyama-zk/iciclegnark v0.1.0 h1:88MkEghzjQBMjrYRJFxZ9oR9CTIpB8NG2zLeCJSvXKQ= +github.com/ingonyama-zk/iciclegnark v0.1.0/go.mod h1:wz6+IpyHKs6UhMMoQpNqz1VY+ddfKqC/gRwR/64W6WU= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -32,13 +35,8 @@ github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= @@ -48,7 +46,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=