diff --git a/Makefile b/Makefile index ee053db8a40..1e653a461b6 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ METADATA_VAR += CommitSHA=$(EXTRA_VERSION) METADATA_VAR += BaseDockerLabel=$(BASE_DOCKER_LABEL) METADATA_VAR += DockerNamespace=$(DOCKER_NS) -GO_VER = 1.16.7 +GO_VER = 1.17.5 GO_TAGS ?= RELEASE_EXES = orderer $(TOOLS_EXES) diff --git a/ci/azure-pipelines-merge.yml b/ci/azure-pipelines-merge.yml index 760af5e3a05..40312a06b90 100644 --- a/ci/azure-pipelines-merge.yml +++ b/ci/azure-pipelines-merge.yml @@ -11,7 +11,7 @@ pr: none variables: GOPATH: $(Agent.BuildDirectory)/go PATH: $(Agent.BuildDirectory)/go/bin:/usr/local/go/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin - GOVER: 1.16.7 + GOVER: 1.17.5 jobs: - job: UnitTests diff --git a/ci/azure-pipelines-release.yml b/ci/azure-pipelines-release.yml index 001e99e8d9d..eafe4e0fad9 100644 --- a/ci/azure-pipelines-release.yml +++ b/ci/azure-pipelines-release.yml @@ -11,7 +11,7 @@ variables: - name: GOPATH value: $(Agent.BuildDirectory)/go - name: GOVER - value: 1.16.7 + value: 1.17.5 stages: - stage: BuildBinaries diff --git a/ci/azure-pipelines.yml b/ci/azure-pipelines.yml index 424258e0c43..1c9fce18248 100644 --- a/ci/azure-pipelines.yml +++ b/ci/azure-pipelines.yml @@ -11,7 +11,7 @@ pr: variables: GOPATH: $(Agent.BuildDirectory)/go PATH: $(Agent.BuildDirectory)/go/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin - GOVER: 1.16.7 + GOVER: 1.17.5 stages: - stage: VerifyBuild diff --git a/cmd/common/signer/signer_test.go b/cmd/common/signer/signer_test.go index 16a3ace43e8..5da1ec1d535 100644 --- a/cmd/common/signer/signer_test.go +++ b/cmd/common/signer/signer_test.go @@ -159,6 +159,6 @@ func TestSignerBadConfig(t *testing.T) { } signer, err = NewSigner(conf) - require.EqualError(t, err, "enrollment certificate is not a valid x509 certificate: asn1: syntax error: data truncated") + require.EqualError(t, err, "enrollment certificate is not a valid x509 certificate: x509: malformed certificate") require.Nil(t, signer) } diff --git a/common/crypto/expiration_test.go b/common/crypto/expiration_test.go index 67c6e113c1a..6284bc11baf 100644 --- a/common/crypto/expiration_test.go +++ b/common/crypto/expiration_test.go @@ -275,14 +275,14 @@ cRv6rqxvy5M+t0DhRtiwCen70YCUsksb }{ { description: "Bad first certificate", - errContains: "asn1:", + errContains: "malformed certificate", first: []byte{1, 2, 3}, second: bob, }, { description: "Bad second certificate", - errContains: "asn1:", + errContains: "malformed certificate", first: alice, second: []byte{1, 2, 3}, }, diff --git a/docs/source/dev-setup/devenv.rst b/docs/source/dev-setup/devenv.rst index 45a5dcc4d75..cb8d0b372c2 100644 --- a/docs/source/dev-setup/devenv.rst +++ b/docs/source/dev-setup/devenv.rst @@ -5,7 +5,7 @@ Prerequisites ~~~~~~~~~~~~~ - `Git client `__ -- `Go `__ version 1.16.x (recommended Go version can be found in project Makefile) +- `Go `__ version 1.17.x (recommended Go version can be found in project Makefile) - `Docker `__ version 18.03 or later - (macOS) `Xcode Command Line Tools `__ - `SoftHSM `__ use version 2.5 as 2.6 is broken in this environment diff --git a/docs/source/prereqs.md b/docs/source/prereqs.md index 3a891f5b455..5f52ac2ef4c 100644 --- a/docs/source/prereqs.md +++ b/docs/source/prereqs.md @@ -82,9 +82,9 @@ Optional: Install the latest Fabric supported version of [Go](https://golang.org installed (only required if you will be writing Go chaincode or SDK applications). ```shell -$ brew install go@1.16.7 +$ brew install go@1.17.5 $ go version -go1.16.7 darwin/amd64 +go1.17.5 darwin/amd64 ``` ### JQ diff --git a/go.mod b/go.mod index 483375653b6..e6419e2a4a6 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.14 require ( code.cloudfoundry.org/clock v1.0.0 github.com/DataDog/zstd v1.4.5 // indirect - github.com/IBM/idemix v0.0.0-20210930104432-e4a1410f5353 + github.com/IBM/idemix v0.0.0-20220112103229-701e7610d405 github.com/Knetic/govaluate v3.0.0+incompatible github.com/Shopify/sarama v1.20.1 github.com/Shopify/toxiproxy v2.1.4+incompatible // indirect @@ -69,3 +69,5 @@ require ( ) replace github.com/onsi/gomega => github.com/onsi/gomega v1.9.0 + +replace github.com/cespare/xxhash/v2 => github.com/cespare/xxhash/v2 v2.1.2 // fix for Go 1.17 in github.com/prometheus/client_golang dependency without updating protobuf diff --git a/go.sum b/go.sum index db8d2eba94c..780f3eb0a22 100644 --- a/go.sum +++ b/go.sum @@ -8,10 +8,10 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= -github.com/IBM/idemix v0.0.0-20210930104432-e4a1410f5353 h1:gOG+V3F5J7gsWzHAoNSHAiYV2o3OvgM7uTrF7BUSt2Y= -github.com/IBM/idemix v0.0.0-20210930104432-e4a1410f5353/go.mod h1:Fazy7pMxGGdXRRSFgTipzH4Q02bIEPatJa3km9H3w78= -github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290 h1:usgCPts8YnOT6ba6CQLPzQ5Yb1crnQ8iU132Zm679IM= -github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290/go.mod h1:grSmaMdY3LbW9QwqMrzuTUCHjES4rzT4Dm7q6yIL9vs= +github.com/IBM/idemix v0.0.0-20220112103229-701e7610d405 h1:7cKDQL0CWDXO9acHJCCc7SUYpMcJ9H9NGVjLvwG66Nc= +github.com/IBM/idemix v0.0.0-20220112103229-701e7610d405/go.mod h1:tBeRCKH37b2OkQRJVomLoYk8OjIMYQm+oRWFiJF0jQI= +github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912 h1:rySf+WTiafw7zS7P8GUcZGDd2nDTgLDUx51aIcCFuX4= +github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912/go.mod h1:WZGhleRZVSAg25iKkiWXHacTkui2CY1cyJMBOgpQwh8= github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Microsoft/go-winio v0.4.15-0.20200908182639-5b44b70ab3ab/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= @@ -42,14 +42,14 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/consensys/bavard v0.1.8-0.20210329205436-c3e862ba4e5f/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.0 h1:KHf7Ta876Ys6L8+i0DLRRKOAa3PfJ8oobAX1CEeIa4A= -github.com/consensys/gnark-crypto v0.4.0/go.mod h1:wK/gpXP9B06qTzTVML71GhKD1ygP9xOzukbI68NJqsQ= +github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.6.0 h1:K48rcIJaX2YkQT2k51EiHIxTynpHsOLHF1FVV+0aS7w= +github.com/consensys/gnark-crypto v0.6.0/go.mod h1:PicAZJP763+7N9LZFfj+MquTXq98pwjD6l8Ry8WdHSU= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 h1:qWj4qVYZ95vLWwqyNJCQg7rDsG5wPdze0UaPolH7DUk= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= @@ -141,6 +141,7 @@ github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/handlers v1.4.0 h1:XulKRWSQK5uChr4pEgSE4Tc/OcmnU9GJuSwdog/tZsA= github.com/gorilla/handlers v1.4.0/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= @@ -213,6 +214,9 @@ github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg= github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= @@ -312,6 +316,7 @@ github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1-0.20210116013205-6990a05d54c2 h1:oevpAKCW58ZYJe1hqfgLqg+1zXmYrQ9xf7HLUdfS+qM= github.com/stretchr/testify v1.7.1-0.20210116013205-6990a05d54c2/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/sykesm/zap-logfmt v0.0.2 h1:czSzn+PIXCOAP/4NAIHTTziIKB8201PzoDkKTn+VR/8= @@ -407,8 +412,8 @@ golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 h1:64ChN/hjER/taL4YJuA+gpLfIMT+/NFherRZixbxOhg= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 h1:EjgCl+fVlIaPJSori0ikSz3uV0DOHKWOJFpv1sAAhBM= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -486,3 +491,4 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/scripts/golinter.sh b/scripts/golinter.sh index 6177926ae3d..438e3a3c764 100755 --- a/scripts/golinter.sh +++ b/scripts/golinter.sh @@ -69,8 +69,9 @@ if [ -n "$OUTPUT" ]; then exit 1 fi +# staticcheck Fabric source files - ignore issues in vendored dependency projects echo "Checking with staticcheck" -OUTPUT="$(staticcheck ./... || true)" +OUTPUT="$(staticcheck ./... | grep -v vendor/ || true)" if [ -n "$OUTPUT" ]; then echo "The following staticcheck issues were flagged" echo "$OUTPUT" diff --git a/vagrant/golang.sh b/vagrant/golang.sh index 218e77c5fda..1423133f0f6 100644 --- a/vagrant/golang.sh +++ b/vagrant/golang.sh @@ -5,7 +5,7 @@ # SPDX-License-Identifier: Apache-2.0 GOROOT='/opt/go' -GO_VERSION=1.16.7 +GO_VERSION=1.17.5 # ---------------------------------------------------------------- # Install Golang diff --git a/vendor/github.com/IBM/idemix/go.mod b/vendor/github.com/IBM/idemix/go.mod index 20a127f3db1..f90834977c3 100644 --- a/vendor/github.com/IBM/idemix/go.mod +++ b/vendor/github.com/IBM/idemix/go.mod @@ -3,7 +3,7 @@ module github.com/IBM/idemix go 1.16 require ( - github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290 + github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20210912230133-d1bdfacee922 // indirect github.com/golang/protobuf v1.3.3 diff --git a/vendor/github.com/IBM/idemix/go.sum b/vendor/github.com/IBM/idemix/go.sum index 76f71af7470..89a8a153f7b 100644 --- a/vendor/github.com/IBM/idemix/go.sum +++ b/vendor/github.com/IBM/idemix/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290 h1:usgCPts8YnOT6ba6CQLPzQ5Yb1crnQ8iU132Zm679IM= -github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290/go.mod h1:grSmaMdY3LbW9QwqMrzuTUCHjES4rzT4Dm7q6yIL9vs= +github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912 h1:rySf+WTiafw7zS7P8GUcZGDd2nDTgLDUx51aIcCFuX4= +github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912/go.mod h1:WZGhleRZVSAg25iKkiWXHacTkui2CY1cyJMBOgpQwh8= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20210912230133-d1bdfacee922 h1:8ypNbf5sd3Sm3cKJ9waOGoQv6dKAFiFty9L6NP1AqJ4= @@ -10,9 +10,9 @@ github.com/alecthomas/units v0.0.0-20210912230133-d1bdfacee922/go.mod h1:OMCwj8V github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/consensys/bavard v0.1.8-0.20210329205436-c3e862ba4e5f/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.0 h1:KHf7Ta876Ys6L8+i0DLRRKOAa3PfJ8oobAX1CEeIa4A= -github.com/consensys/gnark-crypto v0.4.0/go.mod h1:wK/gpXP9B06qTzTVML71GhKD1ygP9xOzukbI68NJqsQ= +github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.6.0 h1:K48rcIJaX2YkQT2k51EiHIxTynpHsOLHF1FVV+0aS7w= +github.com/consensys/gnark-crypto v0.6.0/go.mod h1:PicAZJP763+7N9LZFfj+MquTXq98pwjD6l8Ry8WdHSU= 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= @@ -31,6 +31,7 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8 h1:BCR8ZlOZ+deUbWxyY6fpoY8LbB7PR5wGGwCTvWQOU2g= github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE= @@ -44,6 +45,9 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -61,6 +65,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1-0.20210116013205-6990a05d54c2 h1:oevpAKCW58ZYJe1hqfgLqg+1zXmYrQ9xf7HLUdfS+qM= github.com/stretchr/testify v1.7.1-0.20210116013205-6990a05d54c2/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/sykesm/zap-logfmt v0.0.2 h1:czSzn+PIXCOAP/4NAIHTTziIKB8201PzoDkKTn+VR/8= @@ -78,6 +83,7 @@ go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -107,8 +113,8 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 h1:64ChN/hjER/taL4YJuA+gpLfIMT+/NFherRZixbxOhg= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 h1:EjgCl+fVlIaPJSori0ikSz3uV0DOHKWOJFpv1sAAhBM= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -153,3 +159,4 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/vendor/github.com/IBM/mathlib/go.mod b/vendor/github.com/IBM/mathlib/go.mod index 5361b7d030c..94deb01ad2f 100644 --- a/vendor/github.com/IBM/mathlib/go.mod +++ b/vendor/github.com/IBM/mathlib/go.mod @@ -3,8 +3,8 @@ module github.com/IBM/mathlib go 1.16 require ( - github.com/consensys/gnark-crypto v0.4.0 + github.com/consensys/gnark-crypto v0.6.0 github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8 github.com/pkg/errors v0.8.1 - github.com/stretchr/testify v1.4.0 + github.com/stretchr/testify v1.7.0 ) diff --git a/vendor/github.com/IBM/mathlib/go.sum b/vendor/github.com/IBM/mathlib/go.sum index c444a6712f3..b75f9392eaf 100644 --- a/vendor/github.com/IBM/mathlib/go.sum +++ b/vendor/github.com/IBM/mathlib/go.sum @@ -1,28 +1,34 @@ -github.com/consensys/bavard v0.1.8-0.20210329205436-c3e862ba4e5f/go.mod h1:Bpd0/3mZuaj6Sj+PqrmIquiOKy397AKGThQPaGzNXAQ= -github.com/consensys/gnark-crypto v0.4.0 h1:KHf7Ta876Ys6L8+i0DLRRKOAa3PfJ8oobAX1CEeIa4A= -github.com/consensys/gnark-crypto v0.4.0/go.mod h1:wK/gpXP9B06qTzTVML71GhKD1ygP9xOzukbI68NJqsQ= +github.com/consensys/bavard v0.1.8-0.20210915155054-088da2f7f54a/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.6.0 h1:K48rcIJaX2YkQT2k51EiHIxTynpHsOLHF1FVV+0aS7w= +github.com/consensys/gnark-crypto v0.6.0/go.mod h1:PicAZJP763+7N9LZFfj+MquTXq98pwjD6l8Ry8WdHSU= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8 h1:BCR8ZlOZ+deUbWxyY6fpoY8LbB7PR5wGGwCTvWQOU2g= github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 h1:64ChN/hjER/taL4YJuA+gpLfIMT+/NFherRZixbxOhg= -golang.org/x/sys v0.0.0-20210326220804-49726bf1d181/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 h1:EjgCl+fVlIaPJSori0ikSz3uV0DOHKWOJFpv1sAAhBM= +golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/vendor/github.com/cespare/xxhash/v2/.travis.yml b/vendor/github.com/cespare/xxhash/v2/.travis.yml deleted file mode 100644 index c516ea88da7..00000000000 --- a/vendor/github.com/cespare/xxhash/v2/.travis.yml +++ /dev/null @@ -1,8 +0,0 @@ -language: go -go: - - "1.x" - - master -env: - - TAGS="" - - TAGS="-tags purego" -script: go test $TAGS -v ./... diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md index 2fd8693c21b..792b4a60b34 100644 --- a/vendor/github.com/cespare/xxhash/v2/README.md +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -1,7 +1,7 @@ # xxhash -[![GoDoc](https://godoc.org/github.com/cespare/xxhash?status.svg)](https://godoc.org/github.com/cespare/xxhash) -[![Build Status](https://travis-ci.org/cespare/xxhash.svg?branch=master)](https://travis-ci.org/cespare/xxhash) +[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) +[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) xxhash is a Go implementation of the 64-bit [xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a @@ -64,4 +64,6 @@ $ go test -benchtime 10s -bench '/xxhash,direct,bytes' - [InfluxDB](https://github.com/influxdata/influxdb) - [Prometheus](https://github.com/prometheus/prometheus) +- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) - [FreeCache](https://github.com/coocood/freecache) +- [FastCache](https://github.com/VictoriaMetrics/fastcache) diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go index db0b35fbe39..15c835d5417 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -193,7 +193,6 @@ func (d *Digest) UnmarshalBinary(b []byte) error { b, d.v4 = consumeUint64(b) b, d.total = consumeUint64(b) copy(d.mem[:], b) - b = b[len(d.mem):] d.n = int(d.total % uint64(len(d.mem))) return nil } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s index d580e32aed4..be8db5bf796 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s @@ -6,7 +6,7 @@ // Register allocation: // AX h -// CX pointer to advance through b +// SI pointer to advance through b // DX n // BX loop end // R8 v1, k1 @@ -16,39 +16,39 @@ // R12 tmp // R13 prime1v // R14 prime2v -// R15 prime4v +// DI prime4v -// round reads from and advances the buffer pointer in CX. +// round reads from and advances the buffer pointer in SI. // It assumes that R13 has prime1v and R14 has prime2v. #define round(r) \ - MOVQ (CX), R12 \ - ADDQ $8, CX \ + MOVQ (SI), R12 \ + ADDQ $8, SI \ IMULQ R14, R12 \ ADDQ R12, r \ ROLQ $31, r \ IMULQ R13, r // mergeRound applies a merge round on the two registers acc and val. -// It assumes that R13 has prime1v, R14 has prime2v, and R15 has prime4v. +// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v. #define mergeRound(acc, val) \ IMULQ R14, val \ ROLQ $31, val \ IMULQ R13, val \ XORQ val, acc \ IMULQ R13, acc \ - ADDQ R15, acc + ADDQ DI, acc // func Sum64(b []byte) uint64 TEXT ·Sum64(SB), NOSPLIT, $0-32 // Load fixed primes. MOVQ ·prime1v(SB), R13 MOVQ ·prime2v(SB), R14 - MOVQ ·prime4v(SB), R15 + MOVQ ·prime4v(SB), DI // Load slice. - MOVQ b_base+0(FP), CX + MOVQ b_base+0(FP), SI MOVQ b_len+8(FP), DX - LEAQ (CX)(DX*1), BX + LEAQ (SI)(DX*1), BX // The first loop limit will be len(b)-32. SUBQ $32, BX @@ -65,14 +65,14 @@ TEXT ·Sum64(SB), NOSPLIT, $0-32 XORQ R11, R11 SUBQ R13, R11 - // Loop until CX > BX. + // Loop until SI > BX. blockLoop: round(R8) round(R9) round(R10) round(R11) - CMPQ CX, BX + CMPQ SI, BX JLE blockLoop MOVQ R8, AX @@ -100,16 +100,16 @@ noBlocks: afterBlocks: ADDQ DX, AX - // Right now BX has len(b)-32, and we want to loop until CX > len(b)-8. + // Right now BX has len(b)-32, and we want to loop until SI > len(b)-8. ADDQ $24, BX - CMPQ CX, BX + CMPQ SI, BX JG fourByte wordLoop: // Calculate k1. - MOVQ (CX), R8 - ADDQ $8, CX + MOVQ (SI), R8 + ADDQ $8, SI IMULQ R14, R8 ROLQ $31, R8 IMULQ R13, R8 @@ -117,18 +117,18 @@ wordLoop: XORQ R8, AX ROLQ $27, AX IMULQ R13, AX - ADDQ R15, AX + ADDQ DI, AX - CMPQ CX, BX + CMPQ SI, BX JLE wordLoop fourByte: ADDQ $4, BX - CMPQ CX, BX + CMPQ SI, BX JG singles - MOVL (CX), R8 - ADDQ $4, CX + MOVL (SI), R8 + ADDQ $4, SI IMULQ R13, R8 XORQ R8, AX @@ -138,19 +138,19 @@ fourByte: singles: ADDQ $4, BX - CMPQ CX, BX + CMPQ SI, BX JGE finalize singlesLoop: - MOVBQZX (CX), R12 - ADDQ $1, CX + MOVBQZX (SI), R12 + ADDQ $1, SI IMULQ ·prime5v(SB), R12 XORQ R12, AX ROLQ $11, AX IMULQ R13, AX - CMPQ CX, BX + CMPQ SI, BX JL singlesLoop finalize: @@ -179,9 +179,9 @@ TEXT ·writeBlocks(SB), NOSPLIT, $0-40 MOVQ ·prime2v(SB), R14 // Load slice. - MOVQ b_base+8(FP), CX + MOVQ b_base+8(FP), SI MOVQ b_len+16(FP), DX - LEAQ (CX)(DX*1), BX + LEAQ (SI)(DX*1), BX SUBQ $32, BX // Load vN from d. @@ -199,7 +199,7 @@ blockLoop: round(R10) round(R11) - CMPQ CX, BX + CMPQ SI, BX JLE blockLoop // Copy vN back to d. @@ -208,8 +208,8 @@ blockLoop: MOVQ R10, 16(AX) MOVQ R11, 24(AX) - // The number of bytes written is CX minus the old base pointer. - SUBQ b_base+8(FP), CX - MOVQ CX, ret+32(FP) + // The number of bytes written is SI minus the old base pointer. + SUBQ b_base+8(FP), SI + MOVQ SI, ret+32(FP) RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go index 53bf76efbc2..376e0ca2e49 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -6,41 +6,52 @@ package xxhash import ( - "reflect" "unsafe" ) -// Notes: +// In the future it's possible that compiler optimizations will make these +// XxxString functions unnecessary by realizing that calls such as +// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205. +// If that happens, even if we keep these functions they can be replaced with +// the trivial safe code. + +// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is: // -// See https://groups.google.com/d/msg/golang-nuts/dcjzJy-bSpw/tcZYBzQqAQAJ -// for some discussion about these unsafe conversions. +// var b []byte +// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) +// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data +// bh.Len = len(s) +// bh.Cap = len(s) // -// In the future it's possible that compiler optimizations will make these -// unsafe operations unnecessary: https://golang.org/issue/2205. +// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough +// weight to this sequence of expressions that any function that uses it will +// not be inlined. Instead, the functions below use a different unsafe +// conversion designed to minimize the inliner weight and allow both to be +// inlined. There is also a test (TestInlining) which verifies that these are +// inlined. // -// Both of these wrapper functions still incur function call overhead since they -// will not be inlined. We could write Go/asm copies of Sum64 and Digest.Write -// for strings to squeeze out a bit more speed. Mid-stack inlining should -// eventually fix this. +// See https://github.com/golang/go/issues/42739 for discussion. // Sum64String computes the 64-bit xxHash digest of s. // It may be faster than Sum64([]byte(s)) by avoiding a copy. func Sum64String(s string) uint64 { - var b []byte - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data - bh.Len = len(s) - bh.Cap = len(s) + b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) return Sum64(b) } // WriteString adds more data to d. It always returns len(s), nil. // It may be faster than Write([]byte(s)) by avoiding a copy. func (d *Digest) WriteString(s string) (n int, err error) { - var b []byte - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data - bh.Len = len(s) - bh.Cap = len(s) - return d.Write(b) + d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))) + // d.Write always returns len(s), nil. + // Ignoring the return output and returning these fixed values buys a + // savings of 6 in the inliner's cost model. + return len(s), nil +} + +// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout +// of the first two words is the same as the layout of a string. +type sliceHeader struct { + s string + cap int } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/bn254.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/bn254.go index e27e9177a4b..c476c70ab0d 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/bn254.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/bn254.go @@ -30,9 +30,6 @@ var twist fptower.E2 // bTwistCurveCoeff b coeff of the twist (defined over Fp2) curve var bTwistCurveCoeff fptower.E2 -// twoInv 1/2 mod p (needed for DoubleStep in Miller loop) -var twoInv fp.Element - // generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) var g1Gen G1Jac var g2Gen G2Jac @@ -69,6 +66,9 @@ var endo struct { // generator of the curve var xGen big.Int +// fixefCoeff t-1 = 6*xGen^2 +var fixedCoeff big.Int + func init() { bCurveCoeff.SetUint64(3) @@ -76,8 +76,6 @@ func init() { twist.A1.SetUint64(1) bTwistCurveCoeff.Inverse(&twist).MulByElement(&bTwistCurveCoeff, &bCurveCoeff) - twoInv.SetOne().Double(&twoInv).Inverse(&twoInv) - g1Gen.X.SetString("1") g1Gen.Y.SetString("2") g1Gen.Z.SetString("1") @@ -114,6 +112,8 @@ func init() { xGen.SetString("4965661367192848881", 10) + fixedCoeff.SetString("147946756881789318990833708069417712966", 10) + } // Generators return the generators of the r-torsion group, resp. in ker(pi-id), ker(Tr) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/doc.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/doc.go new file mode 100644 index 00000000000..df3457ba2ae --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/doc.go @@ -0,0 +1,18 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package bn254 efficient elliptic curve and pairing implementation for bn254. +package bn254 diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm.go index 715bc7ac121..7344271ebee 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm.go @@ -1,3 +1,4 @@ +//go:build !noadx // +build !noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm_noadx.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm_noadx.go index 371bfeaeb3a..ae778bd3a1f 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm_noadx.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/asm_noadx.go @@ -1,3 +1,4 @@ +//go:build noadx // +build noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/doc.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/doc.go new file mode 100644 index 00000000000..7961440127d --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/doc.go @@ -0,0 +1,43 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package fp contains field arithmetic operations for modulus = 0x30644e...7cfd47. +// +// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) +// +// The modulus is hardcoded in all the operations. +// +// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: +// type Element [4]uint64 +// +// Example API signature +// // Mul z = x * y mod q +// func (z *Element) Mul(x, y *Element) *Element +// +// and can be used like so: +// var a, b Element +// a.SetUint64(2) +// b.SetString("984896738") +// a.Mul(a, b) +// a.Sub(a, a) +// .Add(a, b) +// .Inv(a) +// b.Exp(b, new(big.Int).SetUint64(42)) +// +// Modulus +// 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 // base 16 +// 21888242871839275222246405745257275088696311157297823662689037894645226208583 // base 10 +package fp diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element.go index 8123dbe14c6..0c5e5a20c88 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element.go @@ -14,7 +14,6 @@ // Code generated by consensys/gnark-crypto DO NOT EDIT -// Package fp contains field arithmetic operations for modulus 21888242871839275222246405745257275088696311157297823662689037894645226208583 package fp // /!\ WARNING /!\ @@ -26,10 +25,13 @@ package fp import ( "crypto/rand" "encoding/binary" + "errors" "io" "math/big" "math/bits" + "reflect" "strconv" + "strings" "sync" ) @@ -61,13 +63,21 @@ func Modulus() *big.Int { } // q (modulus) +const qElementWord0 uint64 = 4332616871279656263 +const qElementWord1 uint64 = 10917124144477883021 +const qElementWord2 uint64 = 13281191951274694749 +const qElementWord3 uint64 = 3486998266802970665 + var qElement = Element{ - 4332616871279656263, - 10917124144477883021, - 13281191951274694749, - 3486998266802970665, + qElementWord0, + qElementWord1, + qElementWord2, + qElementWord3, } +// Used for Montgomery reduction. (qInvNeg) q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r +const qInvNegLsw uint64 = 9786893198990664585 + // rSquare var rSquare = Element{ 17522657719365597833, @@ -86,12 +96,39 @@ func init() { _modulus.SetString("21888242871839275222246405745257275088696311157297823662689037894645226208583", 10) } -// SetUint64 z = v, sets z LSB to v (non-Montgomery form) and convert z to Montgomery form +// NewElement returns a new Element from a uint64 value +// +// it is equivalent to +// var v NewElement +// v.SetUint64(...) +func NewElement(v uint64) Element { + z := Element{v} + z.Mul(&z, &rSquare) + return z +} + +// SetUint64 sets z to v and returns z func (z *Element) SetUint64(v uint64) *Element { + // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form *z = Element{v} return z.Mul(z, &rSquare) // z.ToMont() } +// SetInt64 sets z to v and returns z +func (z *Element) SetInt64(v int64) *Element { + + // absolute value of v + m := v >> 63 + z.SetUint64(uint64((v ^ m) - m)) + + if m != 0 { + // v is negative + z.Neg(z) + } + + return z +} + // Set z = x func (z *Element) Set(x *Element) *Element { z[0] = x[0] @@ -101,28 +138,46 @@ func (z *Element) Set(x *Element) *Element { return z } -// SetInterface converts i1 from uint64, int, string, or Element, big.Int into Element -// panic if provided type is not supported -func (z *Element) SetInterface(i1 interface{}) *Element { +// SetInterface converts provided interface into Element +// returns an error if provided type is not supported +// supported types: Element, *Element, uint64, int, string (interpreted as base10 integer), +// *big.Int, big.Int, []byte +func (z *Element) SetInterface(i1 interface{}) (*Element, error) { switch c1 := i1.(type) { case Element: - return z.Set(&c1) + return z.Set(&c1), nil case *Element: - return z.Set(c1) + return z.Set(c1), nil + case uint8: + return z.SetUint64(uint64(c1)), nil + case uint16: + return z.SetUint64(uint64(c1)), nil + case uint32: + return z.SetUint64(uint64(c1)), nil + case uint: + return z.SetUint64(uint64(c1)), nil case uint64: - return z.SetUint64(c1) + return z.SetUint64(c1), nil + case int8: + return z.SetInt64(int64(c1)), nil + case int16: + return z.SetInt64(int64(c1)), nil + case int32: + return z.SetInt64(int64(c1)), nil + case int64: + return z.SetInt64(c1), nil case int: - return z.SetString(strconv.Itoa(c1)) + return z.SetInt64(int64(c1)), nil case string: - return z.SetString(c1) + return z.SetString(c1), nil case *big.Int: - return z.SetBigInt(c1) + return z.SetBigInt(c1), nil case big.Int: - return z.SetBigInt(&c1) + return z.SetBigInt(&c1), nil case []byte: - return z.SetBytes(c1) + return z.SetBytes(c1), nil default: - panic("invalid type") + return nil, errors.New("can't set fp.Element from type " + reflect.TypeOf(i1).String()) } } @@ -152,6 +207,16 @@ func (z *Element) Div(x, y *Element) *Element { return z } +// Bit returns the i'th bit, with lsb == bit 0. +// It is the responsability of the caller to convert from Montgomery to Regular form if needed +func (z *Element) Bit(i uint64) uint64 { + j := i / 64 + if j >= 4 { + return 0 + } + return uint64(z[j] >> (i % 64) & 1) +} + // Equal returns z == x func (z *Element) Equal(x *Element) bool { return (z[3] == x[3]) && (z[2] == x[2]) && (z[1] == x[1]) && (z[0] == x[0]) @@ -162,6 +227,11 @@ func (z *Element) IsZero() bool { return (z[3] | z[2] | z[1] | z[0]) == 0 } +// IsUint64 reports whether z can be represented as an uint64. +func (z *Element) IsUint64() bool { + return (z[3] | z[2] | z[1]) == 0 +} + // Cmp compares (lexicographic order) z and x and returns: // // -1 if z < x @@ -227,7 +297,7 @@ func (z *Element) SetRandom() (*Element, error) { z[3] = binary.BigEndian.Uint64(bytes[24:32]) z[3] %= 3486998266802970665 - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -247,35 +317,39 @@ func One() Element { return one } -// MulAssign is deprecated -// Deprecated: use Mul instead -func (z *Element) MulAssign(x *Element) *Element { - return z.Mul(z, x) -} +// Halve sets z to z / 2 (mod p) +func (z *Element) Halve() { + if z[0]&1 == 1 { + var carry uint64 -// AddAssign is deprecated -// Deprecated: use Add instead -func (z *Element) AddAssign(x *Element) *Element { - return z.Add(z, x) -} + // z = z + q + z[0], carry = bits.Add64(z[0], 4332616871279656263, 0) + z[1], carry = bits.Add64(z[1], 10917124144477883021, carry) + z[2], carry = bits.Add64(z[2], 13281191951274694749, carry) + z[3], _ = bits.Add64(z[3], 3486998266802970665, carry) + + } + + // z = z >> 1 + + z[0] = z[0]>>1 | z[1]<<63 + z[1] = z[1]>>1 | z[2]<<63 + z[2] = z[2]>>1 | z[3]<<63 + z[3] >>= 1 -// SubAssign is deprecated -// Deprecated: use Sub instead -func (z *Element) SubAssign(x *Element) *Element { - return z.Sub(z, x) } // API with assembly impl // Mul z = x * y mod q -// see https://hackmd.io/@zkteam/modular_multiplication +// see https://hackmd.io/@gnark/modular_multiplication func (z *Element) Mul(x, y *Element) *Element { mul(z, x, y) return z } // Square z = x * x mod q -// see https://hackmd.io/@zkteam/modular_multiplication +// see https://hackmd.io/@gnark/modular_multiplication func (z *Element) Square(x *Element) *Element { mul(z, x, x) return z @@ -371,7 +445,58 @@ func _mulGeneric(z, x, y *Element) { z[3], z[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1]) } - // if z > q --> z -= q + // if z > q → z -= q + // note: this is NOT constant time + if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 4332616871279656263, 0) + z[1], b = bits.Sub64(z[1], 10917124144477883021, b) + z[2], b = bits.Sub64(z[2], 13281191951274694749, b) + z[3], _ = bits.Sub64(z[3], 3486998266802970665, b) + } +} + +func _mulWGeneric(z, x *Element, y uint64) { + + var t [4]uint64 + { + // round 0 + c1, c0 := bits.Mul64(y, x[0]) + m := c0 * 9786893198990664585 + c2 := madd0(m, 4332616871279656263, c0) + c1, c0 = madd1(y, x[1], c1) + c2, t[0] = madd2(m, 10917124144477883021, c2, c0) + c1, c0 = madd1(y, x[2], c1) + c2, t[1] = madd2(m, 13281191951274694749, c2, c0) + c1, c0 = madd1(y, x[3], c1) + t[3], t[2] = madd3(m, 3486998266802970665, c0, c2, c1) + } + { + // round 1 + m := t[0] * 9786893198990664585 + c2 := madd0(m, 4332616871279656263, t[0]) + c2, t[0] = madd2(m, 10917124144477883021, c2, t[1]) + c2, t[1] = madd2(m, 13281191951274694749, c2, t[2]) + t[3], t[2] = madd2(m, 3486998266802970665, t[3], c2) + } + { + // round 2 + m := t[0] * 9786893198990664585 + c2 := madd0(m, 4332616871279656263, t[0]) + c2, t[0] = madd2(m, 10917124144477883021, c2, t[1]) + c2, t[1] = madd2(m, 13281191951274694749, c2, t[2]) + t[3], t[2] = madd2(m, 3486998266802970665, t[3], c2) + } + { + // round 3 + m := t[0] * 9786893198990664585 + c2 := madd0(m, 4332616871279656263, t[0]) + c2, z[0] = madd2(m, 10917124144477883021, c2, t[1]) + c2, z[1] = madd2(m, 13281191951274694749, c2, t[2]) + z[3], z[2] = madd2(m, 3486998266802970665, t[3], c2) + } + + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -422,7 +547,7 @@ func _fromMontGeneric(z *Element) { z[3] = C } - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -441,7 +566,7 @@ func _addGeneric(z, x, y *Element) { z[2], carry = bits.Add64(x[2], y[2], carry) z[3], _ = bits.Add64(x[3], y[3], carry) - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -460,7 +585,7 @@ func _doubleGeneric(z, x *Element) { z[2], carry = bits.Add64(x[2], x[2], carry) z[3], _ = bits.Add64(x[3], x[3], carry) - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -500,7 +625,7 @@ func _negGeneric(z, x *Element) { func _reduceGeneric(z *Element) { - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { var b uint64 @@ -528,8 +653,64 @@ func mulByConstant(z *Element, c uint8) { _z := *z z.Double(z).Double(z).Add(z, &_z) default: - panic("not implemented") + var y Element + y.SetUint64(uint64(c)) + z.Mul(z, &y) + } +} + +// BatchInvert returns a new slice with every element inverted. +// Uses Montgomery batch inversion trick +func BatchInvert(a []Element) []Element { + res := make([]Element, len(a)) + if len(a) == 0 { + return res } + + zeroes := make([]bool, len(a)) + accumulator := One() + + for i := 0; i < len(a); i++ { + if a[i].IsZero() { + zeroes[i] = true + continue + } + res[i] = accumulator + accumulator.Mul(&accumulator, &a[i]) + } + + accumulator.Inverse(&accumulator) + + for i := len(a) - 1; i >= 0; i-- { + if zeroes[i] { + continue + } + res[i].Mul(&res[i], &accumulator) + accumulator.Mul(&accumulator, &a[i]) + } + + return res +} + +func _butterflyGeneric(a, b *Element) { + t := *a + a.Add(a, b) + b.Sub(&t, b) +} + +// BitLen returns the minimum number of bits needed to represent z +// returns 0 if z == 0 +func (z *Element) BitLen() int { + if z[3] != 0 { + return 192 + bits.Len64(z[3]) + } + if z[2] != 0 { + return 128 + bits.Len64(z[2]) + } + if z[1] != 0 { + return 64 + bits.Len64(z[1]) + } + return bits.Len64(z[0]) } // Exp z = x^exponent mod q @@ -552,7 +733,7 @@ func (z *Element) Exp(x Element, exponent *big.Int) *Element { } // ToMont converts z to Montgomery form -// sets and returns z = z * r^2 +// sets and returns z = z * r² func (z *Element) ToMont() *Element { return z.Mul(z, &rSquare) } @@ -562,11 +743,41 @@ func (z Element) ToRegular() Element { return *z.FromMont() } -// String returns the string form of an Element in Montgomery form +// String returns the decimal representation of z as generated by +// z.Text(10). func (z *Element) String() string { + return z.Text(10) +} + +// Text returns the string representation of z in the given base. +// Base must be between 2 and 36, inclusive. The result uses the +// lower-case letters 'a' to 'z' for digit values 10 to 35. +// No prefix (such as "0x") is added to the string. If z is a nil +// pointer it returns "". +// If base == 10 and -z fits in a uint64 prefix "-" is added to the string. +func (z *Element) Text(base int) string { + if base < 2 || base > 36 { + panic("invalid base") + } + if z == nil { + return "" + } + zz := *z + zz.FromMont() + if zz.IsUint64() { + return strconv.FormatUint(zz[0], base) + } else if base == 10 { + var zzNeg Element + zzNeg.Neg(z) + zzNeg.FromMont() + if zzNeg.IsUint64() { + return "-" + strconv.FormatUint(zzNeg[0], base) + } + } vv := bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(vv) - return z.ToBigIntRegular(vv).String() + r := zz.ToBigInt(vv).Text(base) + bigIntPool.Put(vv) + return r } // ToBigInt returns z as a big.Int in Montgomery form @@ -598,6 +809,13 @@ func (z *Element) Bytes() (res [Limbs * 8]byte) { return } +// Marshal returns the regular (non montgomery) value +// of z as a big-endian byte slice. +func (z *Element) Marshal() []byte { + b := z.Bytes() + return b[:] +} + // SetBytes interprets e as the bytes of a big-endian unsigned integer, // sets z to that value (in Montgomery form), and returns z. func (z *Element) SetBytes(e []byte) *Element { @@ -645,7 +863,7 @@ func (z *Element) SetBigInt(v *big.Int) *Element { return z } -// setBigInt assumes 0 <= v < q +// setBigInt assumes 0 ⩽ v < q func (z *Element) setBigInt(v *big.Int) *Element { vBits := v.Bits() @@ -666,14 +884,30 @@ func (z *Element) setBigInt(v *big.Int) *Element { return z.ToMont() } -// SetString creates a big.Int with s (in base 10) and calls SetBigInt on z -func (z *Element) SetString(s string) *Element { +// SetString creates a big.Int with number and calls SetBigInt on z +// +// The number prefix determines the actual base: A prefix of +// ''0b'' or ''0B'' selects base 2, ''0'', ''0o'' or ''0O'' selects base 8, +// and ''0x'' or ''0X'' selects base 16. Otherwise, the selected base is 10 +// and no prefix is accepted. +// +// For base 16, lower and upper case letters are considered the same: +// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. +// +// An underscore character ''_'' may appear between a base +// prefix and an adjacent digit, and between successive digits; such +// underscores do not change the value of the number. +// Incorrect placement of underscores is reported as a panic if there +// are no other errors. +// +func (z *Element) SetString(number string) *Element { // get temporary big int from the pool vv := bigIntPool.Get().(*big.Int) - if _, ok := vv.SetString(s, 10); !ok { - panic("Element.SetString failed -> can't parse number in base10 into a big.Int") + if _, ok := vv.SetString(number, 0); !ok { + panic("Element.SetString failed -> can't parse number into a big.Int " + number) } + z.SetBigInt(vv) // release object into pool @@ -682,22 +916,59 @@ func (z *Element) SetString(s string) *Element { return z } -var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int -) +// MarshalJSON returns json encoding of z (z.Text(10)) +// If z == nil, returns null +func (z *Element) MarshalJSON() ([]byte, error) { + if z == nil { + return []byte("null"), nil + } + const maxSafeBound = 15 // we encode it as number if it's small + s := z.Text(10) + if len(s) <= maxSafeBound { + return []byte(s), nil + } + var sbb strings.Builder + sbb.WriteByte('"') + sbb.WriteString(s) + sbb.WriteByte('"') + return []byte(sbb.String()), nil +} -func init() { - _bLegendreExponentElement, _ = new(big.Int).SetString("183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3", 16) - const sqrtExponentElement = "c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) +// UnmarshalJSON accepts numbers and strings as input +// See Element.SetString for valid prefixes (0x, 0b, ...) +func (z *Element) UnmarshalJSON(data []byte) error { + s := string(data) + if len(s) > Bits*3 { + return errors.New("value too large (max = Element.Bits * 3)") + } + + // we accept numbers and strings, remove leading and trailing quotes if any + if len(s) > 0 && s[0] == '"' { + s = s[1:] + } + if len(s) > 0 && s[len(s)-1] == '"' { + s = s[:len(s)-1] + } + + // get temporary big int from the pool + vv := bigIntPool.Get().(*big.Int) + + if _, ok := vv.SetString(s, 0); !ok { + return errors.New("can't parse into a big.Int: " + s) + } + + z.SetBigInt(vv) + + // release object into pool + bigIntPool.Put(vv) + return nil } // Legendre returns the Legendre symbol of z (either +1, -1, or 0.) func (z *Element) Legendre() int { var l Element // z^((q-1)/2) - l.Exp(*z, _bLegendreExponentElement) + l.expByLegendreExp(*z) if l.IsZero() { return 0 @@ -717,7 +988,7 @@ func (z *Element) Sqrt(x *Element) *Element { // q ≡ 3 (mod 4) // using z ≡ ± x^((p+1)/4) (mod q) var y, square Element - y.Exp(*x, _bSqrtExponentElement) + y.expBySqrtExp(*x) // as we didn't compute the legendre symbol, ensure we found y such that y * y = x square.Square(&y) if square.Equal(x) { @@ -726,170 +997,418 @@ func (z *Element) Sqrt(x *Element) *Element { return nil } -// Inverse z = x^-1 mod q -// Algorithm 16 in "Efficient Software-Implementation of Finite Fields with Applications to Cryptography" -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - if x.IsZero() { - return z.Set(x) +func max(a int, b int) int { + if a > b { + return a } + return b +} - // initialize u = q - var u = Element{ - 4332616871279656263, - 10917124144477883021, - 13281191951274694749, - 3486998266802970665, +func min(a int, b int) int { + if a < b { + return a } + return b +} + +const updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) +const updateFactorIdentityMatrixRow0 = 1 +const updateFactorIdentityMatrixRow1 = 1 << 32 + +func updateFactorsDecompose(c int64) (int64, int64) { + c += updateFactorsConversionBias + const low32BitsFilter int64 = 0xFFFFFFFF + f := c&low32BitsFilter - 0x7FFFFFFF + g := c>>32&low32BitsFilter - 0x7FFFFFFF + return f, g +} + +const k = 32 // word size / 2 +const signBitSelector = uint64(1) << 63 +const approxLowBitsN = k - 1 +const approxHighBitsN = k + 1 +const inversionCorrectionFactorWord0 = 11111708840330028223 +const inversionCorrectionFactorWord1 = 3098618286181893933 +const inversionCorrectionFactorWord2 = 756602578711705709 +const inversionCorrectionFactorWord3 = 1041752015607019851 - // initialize s = r^2 - var s = Element{ - 17522657719365597833, - 13107472804851548667, - 5164255478447964150, - 493319470278259999, +const invIterationsN = 18 + +// Inverse z = x⁻¹ mod q +// Implements "Optimized Binary GCD for Modular Inversion" +// https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf +func (z *Element) Inverse(x *Element) *Element { + if x.IsZero() { + z.SetZero() + return z } - // r = 0 - r := Element{} + a := *x + b := Element{ + qElementWord0, + qElementWord1, + qElementWord2, + qElementWord3, + } // b := q + + u := Element{1} - v := *x + // Update factors: we get [u; v]:= [f0 g0; f1 g1] [u; v] + // c_i = f_i + 2³¹ - 1 + 2³² * (g_i + 2³¹ - 1) + var c0, c1 int64 - var carry, borrow, t, t2 uint64 - var bigger bool + // Saved update factors to reduce the number of field multiplications + var pf0, pf1, pg0, pg1 int64 - for { - for v[0]&1 == 0 { + var i uint - // v = v >> 1 - t2 = v[3] << 63 - v[3] >>= 1 - t = t2 - t2 = v[2] << 63 - v[2] = (v[2] >> 1) | t - t = t2 - t2 = v[1] << 63 - v[1] = (v[1] >> 1) | t - t = t2 - v[0] = (v[0] >> 1) | t + var v, s Element - if s[0]&1 == 1 { + // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations + // This also lets us get away with half as many updates to u,v + // To make this constant-time-ish, replace the condition with i < invIterationsN + for i = 0; i&1 == 1 || !a.IsZero(); i++ { + n := max(a.BitLen(), b.BitLen()) + aApprox, bApprox := approximate(&a, n), approximate(&b, n) - // s = s + q - s[0], carry = bits.Add64(s[0], 4332616871279656263, 0) - s[1], carry = bits.Add64(s[1], 10917124144477883021, carry) - s[2], carry = bits.Add64(s[2], 13281191951274694749, carry) - s[3], _ = bits.Add64(s[3], 3486998266802970665, carry) + // After 0 iterations, we have f₀ ≤ 2⁰ and f₁ < 2⁰ + // f0, g0, f1, g1 = 1, 0, 0, 1 + c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 + for j := 0; j < approxLowBitsN; j++ { + + if aApprox&1 == 0 { + aApprox /= 2 + } else { + s, borrow := bits.Sub64(aApprox, bApprox, 0) + if borrow == 1 { + s = bApprox - aApprox + bApprox = aApprox + c0, c1 = c1, c0 + } + + aApprox = s / 2 + c0 = c0 - c1 + + // Now |f₀| < 2ʲ + 2ʲ = 2ʲ⁺¹ + // |f₁| ≤ 2ʲ still } - // s = s >> 1 - t2 = s[3] << 63 - s[3] >>= 1 - t = t2 - t2 = s[2] << 63 - s[2] = (s[2] >> 1) | t - t = t2 - t2 = s[1] << 63 - s[1] = (s[1] >> 1) | t - t = t2 - s[0] = (s[0] >> 1) | t + c1 *= 2 + // |f₁| ≤ 2ʲ⁺¹ + } + + s = a + var g0 int64 + // from this point on c0 aliases for f0 + c0, g0 = updateFactorsDecompose(c0) + aHi := a.linearCombNonModular(&s, c0, &b, g0) + if aHi&signBitSelector != 0 { + // if aHi < 0 + c0, g0 = -c0, -g0 + aHi = a.neg(&a, aHi) + } + // right-shift a by k-1 bits + a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) + a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) + a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) + a[3] = (a[3] >> approxLowBitsN) | (aHi << approxHighBitsN) + + var f1 int64 + // from this point on c1 aliases for g0 + f1, c1 = updateFactorsDecompose(c1) + bHi := b.linearCombNonModular(&s, f1, &b, c1) + if bHi&signBitSelector != 0 { + // if bHi < 0 + f1, c1 = -f1, -c1 + bHi = b.neg(&b, bHi) } - for u[0]&1 == 0 { - - // u = u >> 1 - t2 = u[3] << 63 - u[3] >>= 1 - t = t2 - t2 = u[2] << 63 - u[2] = (u[2] >> 1) | t - t = t2 - t2 = u[1] << 63 - u[1] = (u[1] >> 1) | t - t = t2 - u[0] = (u[0] >> 1) | t - - if r[0]&1 == 1 { - - // r = r + q - r[0], carry = bits.Add64(r[0], 4332616871279656263, 0) - r[1], carry = bits.Add64(r[1], 10917124144477883021, carry) - r[2], carry = bits.Add64(r[2], 13281191951274694749, carry) - r[3], _ = bits.Add64(r[3], 3486998266802970665, carry) + // right-shift b by k-1 bits + b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) + b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) + b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) + b[3] = (b[3] >> approxLowBitsN) | (bHi << approxHighBitsN) + + if i&1 == 1 { + // Combine current update factors with previously stored ones + // [f₀, g₀; f₁, g₁] ← [f₀, g₀; f₁, g₀] [pf₀, pg₀; pf₀, pg₀] + // We have |f₀|, |g₀|, |pf₀|, |pf₁| ≤ 2ᵏ⁻¹, and that |pf_i| < 2ᵏ⁻¹ for i ∈ {0, 1} + // Then for the new value we get |f₀| < 2ᵏ⁻¹ × 2ᵏ⁻¹ + 2ᵏ⁻¹ × 2ᵏ⁻¹ = 2²ᵏ⁻¹ + // Which leaves us with an extra bit for the sign + + // c0 aliases f0, c1 aliases g1 + c0, g0, f1, c1 = c0*pf0+g0*pf1, + c0*pg0+g0*pg1, + f1*pf0+c1*pf1, + f1*pg0+c1*pg1 + + s = u + u.linearCombSosSigned(&u, c0, &v, g0) + v.linearCombSosSigned(&s, f1, &v, c1) - } + } else { + // Save update factors + pf0, pg0, pf1, pg1 = c0, g0, f1, c1 + } + } + + // For every iteration that we miss, v is not being multiplied by 2²ᵏ⁻² + const pSq int64 = 1 << (2 * (k - 1)) + // If the function is constant-time ish, this loop will not run (probably no need to take it out explicitly) + for ; i < invIterationsN; i += 2 { + v.mulWSigned(&v, pSq) + } + + z.Mul(&v, &Element{ + inversionCorrectionFactorWord0, + inversionCorrectionFactorWord1, + inversionCorrectionFactorWord2, + inversionCorrectionFactorWord3, + }) + return z +} + +// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits +// if x fits in a word as is, no approximation necessary +func approximate(x *Element, nBits int) uint64 { + + if nBits <= 64 { + return x[0] + } + + const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones + lo := mask & x[0] + + hiWordIndex := (nBits - 1) / 64 + + hiWordBitsAvailable := nBits - hiWordIndex*64 + hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) + + mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) + hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) + + mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) + mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed + + return lo | mid | hi +} + +func (z *Element) linearCombSosSigned(x *Element, xC int64, y *Element, yC int64) { + hi := z.linearCombNonModular(x, xC, y, yC) + z.montReduceSigned(z, hi) +} + +// montReduceSigned SOS algorithm; xHi must be at most 63 bits long. Last bit of xHi may be used as a sign bit +func (z *Element) montReduceSigned(x *Element, xHi uint64) { + + const signBitRemover = ^signBitSelector + neg := xHi&signBitSelector != 0 + // the SOS implementation requires that most significant bit is 0 + // Let X be xHi*r + x + // note that if X is negative we would have initially stored it as 2⁶⁴ r + X + xHi &= signBitRemover + // with this a negative X is now represented as 2⁶³ r + X + + var t [2*Limbs - 1]uint64 + var C uint64 + + m := x[0] * qInvNegLsw - // r = r >> 1 - t2 = r[3] << 63 - r[3] >>= 1 - t = t2 - t2 = r[2] << 63 - r[2] = (r[2] >> 1) | t - t = t2 - t2 = r[1] << 63 - r[1] = (r[1] >> 1) | t - t = t2 - r[0] = (r[0] >> 1) | t + C = madd0(m, qElementWord0, x[0]) + C, t[1] = madd2(m, qElementWord1, x[1], C) + C, t[2] = madd2(m, qElementWord2, x[2], C) + C, t[3] = madd2(m, qElementWord3, x[3], C) + // the high word of m * qElement[3] is at most 62 bits + // x[3] + C is at most 65 bits (high word at most 1 bit) + // Thus the resulting C will be at most 63 bits + t[4] = xHi + C + // xHi and C are 63 bits, therefore no overflow + + { + const i = 1 + m = t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, t[i+1] = madd2(m, qElementWord1, t[i+1], C) + C, t[i+2] = madd2(m, qElementWord2, t[i+2], C) + C, t[i+3] = madd2(m, qElementWord3, t[i+3], C) + + t[i+Limbs] += C + } + { + const i = 2 + m = t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, t[i+1] = madd2(m, qElementWord1, t[i+1], C) + C, t[i+2] = madd2(m, qElementWord2, t[i+2], C) + C, t[i+3] = madd2(m, qElementWord3, t[i+3], C) + + t[i+Limbs] += C + } + { + const i = 3 + m := t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, z[0] = madd2(m, qElementWord1, t[i+1], C) + C, z[1] = madd2(m, qElementWord2, t[i+2], C) + z[3], z[2] = madd2(m, qElementWord3, t[i+3], C) + } + + // if z > q → z -= q + // note: this is NOT constant time + if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 10917124144477883021 || (z[1] == 10917124144477883021 && (z[0] < 4332616871279656263))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 4332616871279656263, 0) + z[1], b = bits.Sub64(z[1], 10917124144477883021, b) + z[2], b = bits.Sub64(z[2], 13281191951274694749, b) + z[3], _ = bits.Sub64(z[3], 3486998266802970665, b) + } + if neg { + // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead + var b uint64 + z[0], b = bits.Sub64(z[0], signBitSelector, 0) + z[1], b = bits.Sub64(z[1], 0, b) + z[2], b = bits.Sub64(z[2], 0, b) + z[3], b = bits.Sub64(z[3], 0, b) + + // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 + if b != 0 { + // z[3] = -1 + // negative: add q + const neg1 = 0xFFFFFFFFFFFFFFFF + + b = 0 + z[0], b = bits.Add64(z[0], qElementWord0, b) + z[1], b = bits.Add64(z[1], qElementWord1, b) + z[2], b = bits.Add64(z[2], qElementWord2, b) + z[3], _ = bits.Add64(neg1, qElementWord3, b) } + } +} - // v >= u - bigger = !(v[3] < u[3] || (v[3] == u[3] && (v[2] < u[2] || (v[2] == u[2] && (v[1] < u[1] || (v[1] == u[1] && (v[0] < u[0]))))))) +// mulWSigned mul word signed (w/ montgomery reduction) +func (z *Element) mulWSigned(x *Element, y int64) { + m := y >> 63 + _mulWGeneric(z, x, uint64((y^m)-m)) + // multiply by abs(y) + if y < 0 { + z.Neg(z) + } +} - if bigger { +func (z *Element) neg(x *Element, xHi uint64) uint64 { + var b uint64 - // v = v - u - v[0], borrow = bits.Sub64(v[0], u[0], 0) - v[1], borrow = bits.Sub64(v[1], u[1], borrow) - v[2], borrow = bits.Sub64(v[2], u[2], borrow) - v[3], _ = bits.Sub64(v[3], u[3], borrow) + z[0], b = bits.Sub64(0, x[0], 0) + z[1], b = bits.Sub64(0, x[1], b) + z[2], b = bits.Sub64(0, x[2], b) + z[3], b = bits.Sub64(0, x[3], b) + xHi, _ = bits.Sub64(0, xHi, b) - // s = s - r - s[0], borrow = bits.Sub64(s[0], r[0], 0) - s[1], borrow = bits.Sub64(s[1], r[1], borrow) - s[2], borrow = bits.Sub64(s[2], r[2], borrow) - s[3], borrow = bits.Sub64(s[3], r[3], borrow) + return xHi +} - if borrow == 1 { +// regular multiplication by one word regular (non montgomery) +// Fewer additions than the branch-free for positive y. Could be faster on some architectures +func (z *Element) mulWRegular(x *Element, y int64) uint64 { - // s = s + q - s[0], carry = bits.Add64(s[0], 4332616871279656263, 0) - s[1], carry = bits.Add64(s[1], 10917124144477883021, carry) - s[2], carry = bits.Add64(s[2], 13281191951274694749, carry) - s[3], _ = bits.Add64(s[3], 3486998266802970665, carry) + // w := abs(y) + m := y >> 63 + w := uint64((y ^ m) - m) - } - } else { + var c uint64 + c, z[0] = bits.Mul64(x[0], w) + c, z[1] = madd1(x[1], w, c) + c, z[2] = madd1(x[2], w, c) + c, z[3] = madd1(x[3], w, c) - // u = u - v - u[0], borrow = bits.Sub64(u[0], v[0], 0) - u[1], borrow = bits.Sub64(u[1], v[1], borrow) - u[2], borrow = bits.Sub64(u[2], v[2], borrow) - u[3], _ = bits.Sub64(u[3], v[3], borrow) + if y < 0 { + c = z.neg(z, c) + } - // r = r - s - r[0], borrow = bits.Sub64(r[0], s[0], 0) - r[1], borrow = bits.Sub64(r[1], s[1], borrow) - r[2], borrow = bits.Sub64(r[2], s[2], borrow) - r[3], borrow = bits.Sub64(r[3], s[3], borrow) + return c +} - if borrow == 1 { +/* +Removed: seems slower +// mulWRegular branch-free regular multiplication by one word (non montgomery) +func (z *Element) mulWRegularBf(x *Element, y int64) uint64 { - // r = r + q - r[0], carry = bits.Add64(r[0], 4332616871279656263, 0) - r[1], carry = bits.Add64(r[1], 10917124144477883021, carry) - r[2], carry = bits.Add64(r[2], 13281191951274694749, carry) - r[3], _ = bits.Add64(r[3], 3486998266802970665, carry) + w := uint64(y) + allNeg := uint64(y >> 63) // -1 if y < 0, 0 o.w - } + // s[0], s[1] so results are not stored immediately in z. + // x[i] will be needed in the i+1 th iteration. We don't want to overwrite it in case x = z + var s [2]uint64 + var h [2]uint64 + + h[0], s[0] = bits.Mul64(x[0], w) + + c := uint64(0) + b := uint64(0) + + { + const curI = 1 % 2 + const prevI = 1 - curI + const iMinusOne = 1 - 1 + + h[curI], s[curI] = bits.Mul64(x[1], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } - if (u[0] == 1) && (u[3]|u[2]|u[1]) == 0 { - return z.Set(&r) + + { + const curI = 2 % 2 + const prevI = 1 - curI + const iMinusOne = 2 - 1 + + h[curI], s[curI] = bits.Mul64(x[2], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } - if (v[0] == 1) && (v[3]|v[2]|v[1]) == 0 { - return z.Set(&s) + + { + const curI = 3 % 2 + const prevI = 1 - curI + const iMinusOne = 3 - 1 + + h[curI], s[curI] = bits.Mul64(x[3], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } + { + const curI = 4 % 2 + const prevI = 1 - curI + const iMinusOne = 3 + + s[curI], _ = bits.Sub64(h[prevI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] + + return s[curI] + c } +}*/ + +// Requires NoCarry +func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { + var yTimes Element + + yHi := yTimes.mulWRegular(y, yC) + xHi := z.mulWRegular(x, xC) + + carry := uint64(0) + z[0], carry = bits.Add64(z[0], yTimes[0], carry) + z[1], carry = bits.Add64(z[1], yTimes[1], carry) + z[2], carry = bits.Add64(z[2], yTimes[2], carry) + z[3], carry = bits.Add64(z[3], yTimes[3], carry) + + yHi, _ = bits.Add64(xHi, yHi, carry) + return yHi } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_exp.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_exp.go new file mode 100644 index 00000000000..315a40b9fb9 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_exp.go @@ -0,0 +1,802 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package fp + +// expBySqrtExp is equivalent to z.Exp(x, c19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52) +// +// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain +func (z *Element) expBySqrtExp(x Element) *Element { + // addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _110 = 1 + _101 + // _111 = 1 + _110 + // _1011 = _101 + _110 + // _1100 = 1 + _1011 + // _1101 = 1 + _1100 + // _1111 = _10 + _1101 + // _10001 = _10 + _1111 + // _10011 = _10 + _10001 + // _10111 = _110 + _10001 + // _11001 = _10 + _10111 + // _11011 = _10 + _11001 + // _11111 = _110 + _11001 + // _100011 = _1100 + _10111 + // _100111 = _1100 + _11011 + // _101001 = _10 + _100111 + // _101011 = _10 + _101001 + // _101101 = _10 + _101011 + // _111001 = _1100 + _101101 + // _1100000 = _100111 + _111001 + // i46 = ((_1100000 << 5 + _11001) << 9 + _100111) << 8 + // i62 = ((_111001 + i46) << 4 + _111) << 9 + _10011 + // i89 = ((i62 << 7 + _1101) << 13 + _101001) << 5 + // i109 = ((_10111 + i89) << 7 + _101) << 10 + _10001 + // i130 = ((i109 << 6 + _11011) << 5 + _1101) << 8 + // i154 = ((_11 + i130) << 12 + _101011) << 9 + _10111 + // i179 = ((i154 << 6 + _11001) << 5 + _1111) << 12 + // i198 = ((_101101 + i179) << 7 + _101001) << 9 + _101101 + // i220 = ((i198 << 7 + _111) << 9 + _111001) << 4 + // i236 = ((_101 + i220) << 7 + _1101) << 6 + _1111 + // i265 = ((i236 << 5 + 1) << 11 + _100011) << 11 + // i281 = ((_101101 + i265) << 4 + _1011) << 9 + _11111 + // i299 = (i281 << 8 + _110 + _111001) << 7 + _101001 + // return 2*i299 + // + // Operations: 246 squares 54 multiplies + + // Allocate Temporaries. + var ( + t0 = new(Element) + t1 = new(Element) + t2 = new(Element) + t3 = new(Element) + t4 = new(Element) + t5 = new(Element) + t6 = new(Element) + t7 = new(Element) + t8 = new(Element) + t9 = new(Element) + t10 = new(Element) + t11 = new(Element) + t12 = new(Element) + t13 = new(Element) + t14 = new(Element) + t15 = new(Element) + t16 = new(Element) + t17 = new(Element) + t18 = new(Element) + ) + + // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18 Element + // Step 1: t4 = x^0x2 + t4.Square(&x) + + // Step 2: t13 = x^0x3 + t13.Mul(&x, t4) + + // Step 3: t8 = x^0x5 + t8.Mul(t4, t13) + + // Step 4: t1 = x^0x6 + t1.Mul(&x, t8) + + // Step 5: t9 = x^0x7 + t9.Mul(&x, t1) + + // Step 6: t3 = x^0xb + t3.Mul(t8, t1) + + // Step 7: t0 = x^0xc + t0.Mul(&x, t3) + + // Step 8: t7 = x^0xd + t7.Mul(&x, t0) + + // Step 9: t6 = x^0xf + t6.Mul(t4, t7) + + // Step 10: t15 = x^0x11 + t15.Mul(t4, t6) + + // Step 11: t16 = x^0x13 + t16.Mul(t4, t15) + + // Step 12: t11 = x^0x17 + t11.Mul(t1, t15) + + // Step 13: t10 = x^0x19 + t10.Mul(t4, t11) + + // Step 14: t14 = x^0x1b + t14.Mul(t4, t10) + + // Step 15: t2 = x^0x1f + t2.Mul(t1, t10) + + // Step 16: t5 = x^0x23 + t5.Mul(t0, t11) + + // Step 17: t17 = x^0x27 + t17.Mul(t0, t14) + + // Step 18: z = x^0x29 + z.Mul(t4, t17) + + // Step 19: t12 = x^0x2b + t12.Mul(t4, z) + + // Step 20: t4 = x^0x2d + t4.Mul(t4, t12) + + // Step 21: t0 = x^0x39 + t0.Mul(t0, t4) + + // Step 22: t18 = x^0x60 + t18.Mul(t17, t0) + + // Step 27: t18 = x^0xc00 + for s := 0; s < 5; s++ { + t18.Square(t18) + } + + // Step 28: t18 = x^0xc19 + t18.Mul(t10, t18) + + // Step 37: t18 = x^0x183200 + for s := 0; s < 9; s++ { + t18.Square(t18) + } + + // Step 38: t17 = x^0x183227 + t17.Mul(t17, t18) + + // Step 46: t17 = x^0x18322700 + for s := 0; s < 8; s++ { + t17.Square(t17) + } + + // Step 47: t17 = x^0x18322739 + t17.Mul(t0, t17) + + // Step 51: t17 = x^0x183227390 + for s := 0; s < 4; s++ { + t17.Square(t17) + } + + // Step 52: t17 = x^0x183227397 + t17.Mul(t9, t17) + + // Step 61: t17 = x^0x30644e72e00 + for s := 0; s < 9; s++ { + t17.Square(t17) + } + + // Step 62: t16 = x^0x30644e72e13 + t16.Mul(t16, t17) + + // Step 69: t16 = x^0x1832273970980 + for s := 0; s < 7; s++ { + t16.Square(t16) + } + + // Step 70: t16 = x^0x183227397098d + t16.Mul(t7, t16) + + // Step 83: t16 = x^0x30644e72e131a000 + for s := 0; s < 13; s++ { + t16.Square(t16) + } + + // Step 84: t16 = x^0x30644e72e131a029 + t16.Mul(z, t16) + + // Step 89: t16 = x^0x60c89ce5c26340520 + for s := 0; s < 5; s++ { + t16.Square(t16) + } + + // Step 90: t16 = x^0x60c89ce5c26340537 + t16.Mul(t11, t16) + + // Step 97: t16 = x^0x30644e72e131a029b80 + for s := 0; s < 7; s++ { + t16.Square(t16) + } + + // Step 98: t16 = x^0x30644e72e131a029b85 + t16.Mul(t8, t16) + + // Step 108: t16 = x^0xc19139cb84c680a6e1400 + for s := 0; s < 10; s++ { + t16.Square(t16) + } + + // Step 109: t15 = x^0xc19139cb84c680a6e1411 + t15.Mul(t15, t16) + + // Step 115: t15 = x^0x30644e72e131a029b850440 + for s := 0; s < 6; s++ { + t15.Square(t15) + } + + // Step 116: t14 = x^0x30644e72e131a029b85045b + t14.Mul(t14, t15) + + // Step 121: t14 = x^0x60c89ce5c263405370a08b60 + for s := 0; s < 5; s++ { + t14.Square(t14) + } + + // Step 122: t14 = x^0x60c89ce5c263405370a08b6d + t14.Mul(t7, t14) + + // Step 130: t14 = x^0x60c89ce5c263405370a08b6d00 + for s := 0; s < 8; s++ { + t14.Square(t14) + } + + // Step 131: t13 = x^0x60c89ce5c263405370a08b6d03 + t13.Mul(t13, t14) + + // Step 143: t13 = x^0x60c89ce5c263405370a08b6d03000 + for s := 0; s < 12; s++ { + t13.Square(t13) + } + + // Step 144: t12 = x^0x60c89ce5c263405370a08b6d0302b + t12.Mul(t12, t13) + + // Step 153: t12 = x^0xc19139cb84c680a6e14116da0605600 + for s := 0; s < 9; s++ { + t12.Square(t12) + } + + // Step 154: t11 = x^0xc19139cb84c680a6e14116da0605617 + t11.Mul(t11, t12) + + // Step 160: t11 = x^0x30644e72e131a029b85045b68181585c0 + for s := 0; s < 6; s++ { + t11.Square(t11) + } + + // Step 161: t10 = x^0x30644e72e131a029b85045b68181585d9 + t10.Mul(t10, t11) + + // Step 166: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb20 + for s := 0; s < 5; s++ { + t10.Square(t10) + } + + // Step 167: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f + t10.Mul(t6, t10) + + // Step 179: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f000 + for s := 0; s < 12; s++ { + t10.Square(t10) + } + + // Step 180: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d + t10.Mul(t4, t10) + + // Step 187: t10 = x^0x30644e72e131a029b85045b68181585d9781680 + for s := 0; s < 7; s++ { + t10.Square(t10) + } + + // Step 188: t10 = x^0x30644e72e131a029b85045b68181585d97816a9 + t10.Mul(z, t10) + + // Step 197: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d5200 + for s := 0; s < 9; s++ { + t10.Square(t10) + } + + // Step 198: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d + t10.Mul(t4, t10) + + // Step 205: t10 = x^0x30644e72e131a029b85045b68181585d97816a91680 + for s := 0; s < 7; s++ { + t10.Square(t10) + } + + // Step 206: t9 = x^0x30644e72e131a029b85045b68181585d97816a91687 + t9.Mul(t9, t10) + + // Step 215: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e00 + for s := 0; s < 9; s++ { + t9.Square(t9) + } + + // Step 216: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e39 + t9.Mul(t0, t9) + + // Step 220: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e390 + for s := 0; s < 4; s++ { + t9.Square(t9) + } + + // Step 221: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e395 + t8.Mul(t8, t9) + + // Step 228: t8 = x^0x30644e72e131a029b85045b68181585d97816a916871ca80 + for s := 0; s < 7; s++ { + t8.Square(t8) + } + + // Step 229: t7 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d + t7.Mul(t7, t8) + + // Step 235: t7 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a340 + for s := 0; s < 6; s++ { + t7.Square(t7) + } + + // Step 236: t6 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f + t6.Mul(t6, t7) + + // Step 241: t6 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e0 + for s := 0; s < 5; s++ { + t6.Square(t6) + } + + // Step 242: t6 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e1 + t6.Mul(&x, t6) + + // Step 253: t6 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0800 + for s := 0; s < 11; s++ { + t6.Square(t6) + } + + // Step 254: t5 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823 + t5.Mul(t5, t6) + + // Step 265: t5 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a78411800 + for s := 0; s < 11; s++ { + t5.Square(t5) + } + + // Step 266: t4 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a7841182d + t4.Mul(t4, t5) + + // Step 270: t4 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a7841182d0 + for s := 0; s < 4; s++ { + t4.Square(t4) + } + + // Step 271: t3 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a7841182db + t3.Mul(t3, t4) + + // Step 280: t3 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b600 + for s := 0; s < 9; s++ { + t3.Square(t3) + } + + // Step 281: t2 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f + t2.Mul(t2, t3) + + // Step 289: t2 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f00 + for s := 0; s < 8; s++ { + t2.Square(t2) + } + + // Step 290: t1 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f06 + t1.Mul(t1, t2) + + // Step 291: t0 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f + t0.Mul(t0, t1) + + // Step 298: t0 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a7841182db0f9f80 + for s := 0; s < 7; s++ { + t0.Square(t0) + } + + // Step 299: z = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3951a7841182db0f9fa9 + z.Mul(z, t0) + + // Step 300: z = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52 + z.Square(z) + + return z +} + +// expByLegendreExp is equivalent to z.Exp(x, 183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3) +// +// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain +func (z *Element) expByLegendreExp(x Element) *Element { + // addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _110 = 1 + _101 + // _1000 = _10 + _110 + // _1101 = _101 + _1000 + // _10010 = _101 + _1101 + // _10011 = 1 + _10010 + // _10100 = 1 + _10011 + // _10111 = _11 + _10100 + // _11100 = _101 + _10111 + // _100000 = _1101 + _10011 + // _100011 = _11 + _100000 + // _101011 = _1000 + _100011 + // _101111 = _10011 + _11100 + // _1000001 = _10010 + _101111 + // _1010011 = _10010 + _1000001 + // _1011011 = _1000 + _1010011 + // _1100001 = _110 + _1011011 + // _1110101 = _10100 + _1100001 + // _10010001 = _11100 + _1110101 + // _10010101 = _100000 + _1110101 + // _10110101 = _100000 + _10010101 + // _10111011 = _110 + _10110101 + // _11000001 = _110 + _10111011 + // _11000011 = _10 + _11000001 + // _11010011 = _10010 + _11000001 + // _11100001 = _100000 + _11000001 + // _11100011 = _10 + _11100001 + // _11100111 = _110 + _11100001 + // i57 = ((_11000001 << 8 + _10010001) << 10 + _11100111) << 7 + // i76 = ((_10111 + i57) << 9 + _10011) << 7 + _1101 + // i109 = ((i76 << 14 + _1010011) << 9 + _11100001) << 8 + // i127 = ((_1000001 + i109) << 10 + _1011011) << 5 + _1101 + // i161 = ((i127 << 8 + _11) << 12 + _101011) << 12 + // i186 = ((_10111011 + i161) << 8 + _101111) << 14 + _10110101 + // i214 = ((i186 << 9 + _10010001) << 5 + _1101) << 12 + // i236 = ((_11100011 + i214) << 8 + _10010101) << 11 + _11010011 + // i268 = ((i236 << 7 + _1100001) << 11 + _100011) << 12 + // i288 = ((_1011011 + i268) << 9 + _11000011) << 8 + _11100111 + // return (i288 << 7 + _1110101) << 5 + _11 + // + // Operations: 246 squares 56 multiplies + + // Allocate Temporaries. + var ( + t0 = new(Element) + t1 = new(Element) + t2 = new(Element) + t3 = new(Element) + t4 = new(Element) + t5 = new(Element) + t6 = new(Element) + t7 = new(Element) + t8 = new(Element) + t9 = new(Element) + t10 = new(Element) + t11 = new(Element) + t12 = new(Element) + t13 = new(Element) + t14 = new(Element) + t15 = new(Element) + t16 = new(Element) + t17 = new(Element) + t18 = new(Element) + t19 = new(Element) + t20 = new(Element) + ) + + // var t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20 Element + // Step 1: t8 = x^0x2 + t8.Square(&x) + + // Step 2: z = x^0x3 + z.Mul(&x, t8) + + // Step 3: t2 = x^0x5 + t2.Mul(t8, z) + + // Step 4: t1 = x^0x6 + t1.Mul(&x, t2) + + // Step 5: t3 = x^0x8 + t3.Mul(t8, t1) + + // Step 6: t9 = x^0xd + t9.Mul(t2, t3) + + // Step 7: t6 = x^0x12 + t6.Mul(t2, t9) + + // Step 8: t18 = x^0x13 + t18.Mul(&x, t6) + + // Step 9: t0 = x^0x14 + t0.Mul(&x, t18) + + // Step 10: t19 = x^0x17 + t19.Mul(z, t0) + + // Step 11: t2 = x^0x1c + t2.Mul(t2, t19) + + // Step 12: t16 = x^0x20 + t16.Mul(t9, t18) + + // Step 13: t4 = x^0x23 + t4.Mul(z, t16) + + // Step 14: t14 = x^0x2b + t14.Mul(t3, t4) + + // Step 15: t12 = x^0x2f + t12.Mul(t18, t2) + + // Step 16: t15 = x^0x41 + t15.Mul(t6, t12) + + // Step 17: t17 = x^0x53 + t17.Mul(t6, t15) + + // Step 18: t3 = x^0x5b + t3.Mul(t3, t17) + + // Step 19: t5 = x^0x61 + t5.Mul(t1, t3) + + // Step 20: t0 = x^0x75 + t0.Mul(t0, t5) + + // Step 21: t10 = x^0x91 + t10.Mul(t2, t0) + + // Step 22: t7 = x^0x95 + t7.Mul(t16, t0) + + // Step 23: t11 = x^0xb5 + t11.Mul(t16, t7) + + // Step 24: t13 = x^0xbb + t13.Mul(t1, t11) + + // Step 25: t20 = x^0xc1 + t20.Mul(t1, t13) + + // Step 26: t2 = x^0xc3 + t2.Mul(t8, t20) + + // Step 27: t6 = x^0xd3 + t6.Mul(t6, t20) + + // Step 28: t16 = x^0xe1 + t16.Mul(t16, t20) + + // Step 29: t8 = x^0xe3 + t8.Mul(t8, t16) + + // Step 30: t1 = x^0xe7 + t1.Mul(t1, t16) + + // Step 38: t20 = x^0xc100 + for s := 0; s < 8; s++ { + t20.Square(t20) + } + + // Step 39: t20 = x^0xc191 + t20.Mul(t10, t20) + + // Step 49: t20 = x^0x3064400 + for s := 0; s < 10; s++ { + t20.Square(t20) + } + + // Step 50: t20 = x^0x30644e7 + t20.Mul(t1, t20) + + // Step 57: t20 = x^0x183227380 + for s := 0; s < 7; s++ { + t20.Square(t20) + } + + // Step 58: t19 = x^0x183227397 + t19.Mul(t19, t20) + + // Step 67: t19 = x^0x30644e72e00 + for s := 0; s < 9; s++ { + t19.Square(t19) + } + + // Step 68: t18 = x^0x30644e72e13 + t18.Mul(t18, t19) + + // Step 75: t18 = x^0x1832273970980 + for s := 0; s < 7; s++ { + t18.Square(t18) + } + + // Step 76: t18 = x^0x183227397098d + t18.Mul(t9, t18) + + // Step 90: t18 = x^0x60c89ce5c2634000 + for s := 0; s < 14; s++ { + t18.Square(t18) + } + + // Step 91: t17 = x^0x60c89ce5c2634053 + t17.Mul(t17, t18) + + // Step 100: t17 = x^0xc19139cb84c680a600 + for s := 0; s < 9; s++ { + t17.Square(t17) + } + + // Step 101: t16 = x^0xc19139cb84c680a6e1 + t16.Mul(t16, t17) + + // Step 109: t16 = x^0xc19139cb84c680a6e100 + for s := 0; s < 8; s++ { + t16.Square(t16) + } + + // Step 110: t15 = x^0xc19139cb84c680a6e141 + t15.Mul(t15, t16) + + // Step 120: t15 = x^0x30644e72e131a029b850400 + for s := 0; s < 10; s++ { + t15.Square(t15) + } + + // Step 121: t15 = x^0x30644e72e131a029b85045b + t15.Mul(t3, t15) + + // Step 126: t15 = x^0x60c89ce5c263405370a08b60 + for s := 0; s < 5; s++ { + t15.Square(t15) + } + + // Step 127: t15 = x^0x60c89ce5c263405370a08b6d + t15.Mul(t9, t15) + + // Step 135: t15 = x^0x60c89ce5c263405370a08b6d00 + for s := 0; s < 8; s++ { + t15.Square(t15) + } + + // Step 136: t15 = x^0x60c89ce5c263405370a08b6d03 + t15.Mul(z, t15) + + // Step 148: t15 = x^0x60c89ce5c263405370a08b6d03000 + for s := 0; s < 12; s++ { + t15.Square(t15) + } + + // Step 149: t14 = x^0x60c89ce5c263405370a08b6d0302b + t14.Mul(t14, t15) + + // Step 161: t14 = x^0x60c89ce5c263405370a08b6d0302b000 + for s := 0; s < 12; s++ { + t14.Square(t14) + } + + // Step 162: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb + t13.Mul(t13, t14) + + // Step 170: t13 = x^0x60c89ce5c263405370a08b6d0302b0bb00 + for s := 0; s < 8; s++ { + t13.Square(t13) + } + + // Step 171: t12 = x^0x60c89ce5c263405370a08b6d0302b0bb2f + t12.Mul(t12, t13) + + // Step 185: t12 = x^0x183227397098d014dc2822db40c0ac2ecbc000 + for s := 0; s < 14; s++ { + t12.Square(t12) + } + + // Step 186: t11 = x^0x183227397098d014dc2822db40c0ac2ecbc0b5 + t11.Mul(t11, t12) + + // Step 195: t11 = x^0x30644e72e131a029b85045b68181585d97816a00 + for s := 0; s < 9; s++ { + t11.Square(t11) + } + + // Step 196: t10 = x^0x30644e72e131a029b85045b68181585d97816a91 + t10.Mul(t10, t11) + + // Step 201: t10 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d5220 + for s := 0; s < 5; s++ { + t10.Square(t10) + } + + // Step 202: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d + t9.Mul(t9, t10) + + // Step 214: t9 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d000 + for s := 0; s < 12; s++ { + t9.Square(t9) + } + + // Step 215: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e3 + t8.Mul(t8, t9) + + // Step 223: t8 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e300 + for s := 0; s < 8; s++ { + t8.Square(t8) + } + + // Step 224: t7 = x^0x60c89ce5c263405370a08b6d0302b0bb2f02d522d0e395 + t7.Mul(t7, t8) + + // Step 235: t7 = x^0x30644e72e131a029b85045b68181585d97816a916871ca800 + for s := 0; s < 11; s++ { + t7.Square(t7) + } + + // Step 236: t6 = x^0x30644e72e131a029b85045b68181585d97816a916871ca8d3 + t6.Mul(t6, t7) + + // Step 243: t6 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e546980 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 244: t5 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e1 + t5.Mul(t5, t6) + + // Step 255: t5 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0800 + for s := 0; s < 11; s++ { + t5.Square(t5) + } + + // Step 256: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823 + t4.Mul(t4, t5) + + // Step 268: t4 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f0823000 + for s := 0; s < 12; s++ { + t4.Square(t4) + } + + // Step 269: t3 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b + t3.Mul(t3, t4) + + // Step 278: t3 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b600 + for s := 0; s < 9; s++ { + t3.Square(t3) + } + + // Step 279: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3 + t2.Mul(t2, t3) + + // Step 287: t2 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c300 + for s := 0; s < 8; s++ { + t2.Square(t2) + } + + // Step 288: t1 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7 + t1.Mul(t1, t2) + + // Step 295: t1 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f380 + for s := 0; s < 7; s++ { + t1.Square(t1) + } + + // Step 296: t0 = x^0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f5 + t0.Mul(t0, t1) + + // Step 301: t0 = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea0 + for s := 0; s < 5; s++ { + t0.Square(t0) + } + + // Step 302: z = x^0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3 + z.Mul(z, t0) + + return z +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_fuzz.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_fuzz.go new file mode 100644 index 00000000000..d18bd4bb40c --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_fuzz.go @@ -0,0 +1,136 @@ +//go:build gofuzz +// +build gofuzz + +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package fp + +import ( + "bytes" + "encoding/binary" + "io" + "math/big" + "math/bits" +) + +const ( + fuzzInteresting = 1 + fuzzNormal = 0 + fuzzDiscard = -1 +) + +// Fuzz arithmetic operations fuzzer +func Fuzz(data []byte) int { + r := bytes.NewReader(data) + + var e1, e2 Element + e1.SetRawBytes(r) + e2.SetRawBytes(r) + + { + // mul assembly + + var c, _c Element + a, _a, b, _b := e1, e1, e2, e2 + c.Mul(&a, &b) + _mulGeneric(&_c, &_a, &_b) + + if !c.Equal(&_c) { + panic("mul asm != mul generic on Element") + } + } + + { + // inverse + inv := e1 + inv.Inverse(&inv) + + var bInv, b1, b2 big.Int + e1.ToBigIntRegular(&b1) + bInv.ModInverse(&b1, Modulus()) + inv.ToBigIntRegular(&b2) + + if b2.Cmp(&bInv) != 0 { + panic("inverse operation doesn't match big int result") + } + } + + { + // a + -a == 0 + a, b := e1, e1 + b.Neg(&b) + a.Add(&a, &b) + if !a.IsZero() { + panic("a + -a != 0") + } + } + + return fuzzNormal + +} + +// SetRawBytes reads up to Bytes (bytes needed to represent Element) from reader +// and interpret it as big endian uint64 +// used for fuzzing purposes only +func (z *Element) SetRawBytes(r io.Reader) { + + buf := make([]byte, 8) + + for i := 0; i < len(z); i++ { + if _, err := io.ReadFull(r, buf); err != nil { + goto eof + } + z[i] = binary.BigEndian.Uint64(buf[:]) + } +eof: + z[3] %= qElement[3] + + if z.BiggerModulus() { + var b uint64 + z[0], b = bits.Sub64(z[0], qElement[0], 0) + z[1], b = bits.Sub64(z[1], qElement[1], b) + z[2], b = bits.Sub64(z[2], qElement[2], b) + z[3], b = bits.Sub64(z[3], qElement[3], b) + } + + return +} + +func (z *Element) BiggerModulus() bool { + if z[3] > qElement[3] { + return true + } + if z[3] < qElement[3] { + return false + } + + if z[2] > qElement[2] { + return true + } + if z[2] < qElement[2] { + return false + } + + if z[1] > qElement[1] { + return true + } + if z[1] < qElement[1] { + return false + } + + return z[0] >= qElement[0] +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_adx_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_adx_amd64.s index 2465dbb936b..65c040e36b6 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_adx_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_adx_amd64.s @@ -46,7 +46,7 @@ GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 TEXT ·mul(SB), NOSPLIT, $0-24 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // however, to benefit from the ADCX and ADOX carry chains // we split the inner loops in 2: // for i=0 to N-1 @@ -72,7 +72,7 @@ TEXT ·mul(SB), NOSPLIT, $0-24 // A -> BP // t[0] -> R14 - // t[1] -> R15 + // t[1] -> R13 // t[2] -> CX // t[3] -> BX // clear the flags @@ -80,11 +80,11 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ 0(R11), DX // (A,t[0]) := x[0]*y[0] + A - MULXQ DI, R14, R15 + MULXQ DI, R14, R13 // (A,t[1]) := x[1]*y[0] + A MULXQ R8, AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := x[2]*y[0] + A MULXQ R9, AX, BX @@ -111,14 +111,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -139,9 +139,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[1] + A ADCXQ BP, CX @@ -171,14 +171,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -199,9 +199,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[2] + A ADCXQ BP, CX @@ -231,14 +231,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -259,9 +259,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[3] + A ADCXQ BP, CX @@ -291,14 +291,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -310,12 +310,12 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADCXQ AX, BX ADOXQ BP, BX - // reduce element(R14,R15,CX,BX) using temp registers (R13,SI,R12,R11) - REDUCE(R14,R15,CX,BX,R13,SI,R12,R11) + // reduce element(R14,R13,CX,BX) using temp registers (SI,R12,R11,DI) + REDUCE(R14,R13,CX,BX,SI,R12,R11,DI) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET @@ -323,7 +323,7 @@ TEXT ·mul(SB), NOSPLIT, $0-24 TEXT ·fromMont(SB), NOSPLIT, $0-8 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // when y = 1 we have: // for i=0 to N-1 // t[i] = x[i] @@ -335,7 +335,7 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 // t[N-1] = C MOVQ res+0(FP), DX MOVQ 0(DX), R14 - MOVQ 8(DX), R15 + MOVQ 8(DX), R13 MOVQ 16(DX), CX MOVQ 24(DX), BX XORQ DX, DX @@ -351,14 +351,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -380,14 +380,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -409,14 +409,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -438,14 +438,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -455,12 +455,12 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 ADCXQ AX, BX ADOXQ AX, BX - // reduce element(R14,R15,CX,BX) using temp registers (SI,DI,R8,R9) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9) + // reduce element(R14,R13,CX,BX) using temp registers (SI,DI,R8,R9) + REDUCE(R14,R13,CX,BX,SI,DI,R8,R9) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_amd64.s index bd64044052f..54af6c35873 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_mul_amd64.s @@ -46,7 +46,7 @@ GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 TEXT ·mul(SB), $24-24 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // however, to benefit from the ADCX and ADOX carry chains // we split the inner loops in 2: // for i=0 to N-1 @@ -75,7 +75,7 @@ TEXT ·mul(SB), $24-24 // A -> BP // t[0] -> R14 - // t[1] -> R15 + // t[1] -> R13 // t[2] -> CX // t[3] -> BX // clear the flags @@ -83,11 +83,11 @@ TEXT ·mul(SB), $24-24 MOVQ 0(R11), DX // (A,t[0]) := x[0]*y[0] + A - MULXQ DI, R14, R15 + MULXQ DI, R14, R13 // (A,t[1]) := x[1]*y[0] + A MULXQ R8, AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := x[2]*y[0] + A MULXQ R9, AX, BX @@ -114,14 +114,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -142,9 +142,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[1] + A ADCXQ BP, CX @@ -174,14 +174,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -202,9 +202,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[2] + A ADCXQ BP, CX @@ -234,14 +234,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -262,9 +262,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[3] + A ADCXQ BP, CX @@ -294,14 +294,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -313,12 +313,12 @@ TEXT ·mul(SB), $24-24 ADCXQ AX, BX ADOXQ BP, BX - // reduce element(R14,R15,CX,BX) using temp registers (R13,SI,R12,R11) - REDUCE(R14,R15,CX,BX,R13,SI,R12,R11) + // reduce element(R14,R13,CX,BX) using temp registers (SI,R12,R11,DI) + REDUCE(R14,R13,CX,BX,SI,R12,R11,DI) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET @@ -337,7 +337,7 @@ TEXT ·fromMont(SB), $8-8 NO_LOCAL_POINTERS // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // when y = 1 we have: // for i=0 to N-1 // t[i] = x[i] @@ -351,7 +351,7 @@ TEXT ·fromMont(SB), $8-8 JNE l2 MOVQ res+0(FP), DX MOVQ 0(DX), R14 - MOVQ 8(DX), R15 + MOVQ 8(DX), R13 MOVQ 16(DX), CX MOVQ 24(DX), BX XORQ DX, DX @@ -367,14 +367,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -396,14 +396,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -425,14 +425,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -454,14 +454,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -471,12 +471,12 @@ TEXT ·fromMont(SB), $8-8 ADCXQ AX, BX ADOXQ AX, BX - // reduce element(R14,R15,CX,BX) using temp registers (SI,DI,R8,R9) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9) + // reduce element(R14,R13,CX,BX) using temp registers (SI,DI,R8,R9) + REDUCE(R14,R13,CX,BX,SI,DI,R8,R9) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.go index 71b26855b49..73a3711ec07 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.go @@ -22,6 +22,9 @@ func MulBy3(x *Element) //go:noescape func MulBy5(x *Element) +//go:noescape +func MulBy13(x *Element) + //go:noescape func add(res, x, y *Element) @@ -42,3 +45,6 @@ func fromMont(res *Element) //go:noescape func reduce(res *Element) + +//go:noescape +func Butterfly(a, b *Element) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.s index 833c807ece8..5b8b1f0e76b 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_amd64.s @@ -233,3 +233,108 @@ TEXT ·MulBy5(SB), NOSPLIT, $0-8 MOVQ BX, 16(AX) MOVQ SI, 24(AX) RET + +// MulBy13(x *Element) +TEXT ·MulBy13(SB), NOSPLIT, $0-8 + MOVQ x+0(FP), AX + MOVQ 0(AX), DX + MOVQ 8(AX), CX + MOVQ 16(AX), BX + MOVQ 24(AX), SI + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (R11,R12,R13,R14) + REDUCE(DX,CX,BX,SI,R11,R12,R13,R14) + + MOVQ DX, R11 + MOVQ CX, R12 + MOVQ BX, R13 + MOVQ SI, R14 + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ R11, DX + ADCQ R12, CX + ADCQ R13, BX + ADCQ R14, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ 0(AX), DX + ADCQ 8(AX), CX + ADCQ 16(AX), BX + ADCQ 24(AX), SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + MOVQ DX, 0(AX) + MOVQ CX, 8(AX) + MOVQ BX, 16(AX) + MOVQ SI, 24(AX) + RET + +// Butterfly(a, b *Element) sets a = a + b; b = a - b +TEXT ·Butterfly(SB), NOSPLIT, $0-16 + MOVQ a+0(FP), AX + MOVQ 0(AX), CX + MOVQ 8(AX), BX + MOVQ 16(AX), SI + MOVQ 24(AX), DI + MOVQ CX, R8 + MOVQ BX, R9 + MOVQ SI, R10 + MOVQ DI, R11 + XORQ AX, AX + MOVQ b+8(FP), DX + ADDQ 0(DX), CX + ADCQ 8(DX), BX + ADCQ 16(DX), SI + ADCQ 24(DX), DI + SUBQ 0(DX), R8 + SBBQ 8(DX), R9 + SBBQ 16(DX), R10 + SBBQ 24(DX), R11 + MOVQ $0x3c208c16d87cfd47, R12 + MOVQ $0x97816a916871ca8d, R13 + MOVQ $0xb85045b68181585d, R14 + MOVQ $0x30644e72e131a029, R15 + CMOVQCC AX, R12 + CMOVQCC AX, R13 + CMOVQCC AX, R14 + CMOVQCC AX, R15 + ADDQ R12, R8 + ADCQ R13, R9 + ADCQ R14, R10 + ADCQ R15, R11 + MOVQ R8, 0(DX) + MOVQ R9, 8(DX) + MOVQ R10, 16(DX) + MOVQ R11, 24(DX) + + // reduce element(CX,BX,SI,DI) using temp registers (R8,R9,R10,R11) + REDUCE(CX,BX,SI,DI,R8,R9,R10,R11) + + MOVQ a+0(FP), AX + MOVQ CX, 0(AX) + MOVQ BX, 8(AX) + MOVQ SI, 16(AX) + MOVQ DI, 24(AX) + RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_noasm.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_noasm.go index e6ced1bf566..fec62891833 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_noasm.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fp/element_ops_noasm.go @@ -1,3 +1,4 @@ +//go:build !amd64 // +build !amd64 // Copyright 2020 ConsenSys Software Inc. @@ -34,6 +35,18 @@ func MulBy5(x *Element) { mulByConstant(x, 5) } +// MulBy13 x *= 13 +func MulBy13(x *Element) { + mulByConstant(x, 13) +} + +// Butterfly sets +// a = a + b +// b = a - b +func Butterfly(a, b *Element) { + _butterflyGeneric(a, b) +} + func mul(z, x, y *Element) { _mulGeneric(z, x, y) } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm.go index f859dd8731d..8241357c459 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm.go @@ -1,3 +1,4 @@ +//go:build !noadx // +build !noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm_noadx.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm_noadx.go index ab9b869b5b4..221beab937e 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm_noadx.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/asm_noadx.go @@ -1,3 +1,4 @@ +//go:build noadx // +build noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/doc.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/doc.go new file mode 100644 index 00000000000..e1dea9c74e4 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/doc.go @@ -0,0 +1,43 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package fr contains field arithmetic operations for modulus = 0x30644e...000001. +// +// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) +// +// The modulus is hardcoded in all the operations. +// +// Field elements are represented as an array, and assumed to be in Montgomery form in all methods: +// type Element [4]uint64 +// +// Example API signature +// // Mul z = x * y mod q +// func (z *Element) Mul(x, y *Element) *Element +// +// and can be used like so: +// var a, b Element +// a.SetUint64(2) +// b.SetString("984896738") +// a.Mul(a, b) +// a.Sub(a, a) +// .Add(a, b) +// .Inv(a) +// b.Exp(b, new(big.Int).SetUint64(42)) +// +// Modulus +// 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 // base 16 +// 21888242871839275222246405745257275088548364400416034343698204186575808495617 // base 10 +package fr diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element.go index 3759baa5848..b323c190684 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element.go @@ -14,7 +14,6 @@ // Code generated by consensys/gnark-crypto DO NOT EDIT -// Package fr contains field arithmetic operations for modulus 21888242871839275222246405745257275088548364400416034343698204186575808495617 package fr // /!\ WARNING /!\ @@ -26,10 +25,13 @@ package fr import ( "crypto/rand" "encoding/binary" + "errors" "io" "math/big" "math/bits" + "reflect" "strconv" + "strings" "sync" ) @@ -61,13 +63,21 @@ func Modulus() *big.Int { } // q (modulus) +const qElementWord0 uint64 = 4891460686036598785 +const qElementWord1 uint64 = 2896914383306846353 +const qElementWord2 uint64 = 13281191951274694749 +const qElementWord3 uint64 = 3486998266802970665 + var qElement = Element{ - 4891460686036598785, - 2896914383306846353, - 13281191951274694749, - 3486998266802970665, + qElementWord0, + qElementWord1, + qElementWord2, + qElementWord3, } +// Used for Montgomery reduction. (qInvNeg) q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r +const qInvNegLsw uint64 = 14042775128853446655 + // rSquare var rSquare = Element{ 1997599621687373223, @@ -86,12 +96,39 @@ func init() { _modulus.SetString("21888242871839275222246405745257275088548364400416034343698204186575808495617", 10) } -// SetUint64 z = v, sets z LSB to v (non-Montgomery form) and convert z to Montgomery form +// NewElement returns a new Element from a uint64 value +// +// it is equivalent to +// var v NewElement +// v.SetUint64(...) +func NewElement(v uint64) Element { + z := Element{v} + z.Mul(&z, &rSquare) + return z +} + +// SetUint64 sets z to v and returns z func (z *Element) SetUint64(v uint64) *Element { + // sets z LSB to v (non-Montgomery form) and convert z to Montgomery form *z = Element{v} return z.Mul(z, &rSquare) // z.ToMont() } +// SetInt64 sets z to v and returns z +func (z *Element) SetInt64(v int64) *Element { + + // absolute value of v + m := v >> 63 + z.SetUint64(uint64((v ^ m) - m)) + + if m != 0 { + // v is negative + z.Neg(z) + } + + return z +} + // Set z = x func (z *Element) Set(x *Element) *Element { z[0] = x[0] @@ -101,28 +138,46 @@ func (z *Element) Set(x *Element) *Element { return z } -// SetInterface converts i1 from uint64, int, string, or Element, big.Int into Element -// panic if provided type is not supported -func (z *Element) SetInterface(i1 interface{}) *Element { +// SetInterface converts provided interface into Element +// returns an error if provided type is not supported +// supported types: Element, *Element, uint64, int, string (interpreted as base10 integer), +// *big.Int, big.Int, []byte +func (z *Element) SetInterface(i1 interface{}) (*Element, error) { switch c1 := i1.(type) { case Element: - return z.Set(&c1) + return z.Set(&c1), nil case *Element: - return z.Set(c1) + return z.Set(c1), nil + case uint8: + return z.SetUint64(uint64(c1)), nil + case uint16: + return z.SetUint64(uint64(c1)), nil + case uint32: + return z.SetUint64(uint64(c1)), nil + case uint: + return z.SetUint64(uint64(c1)), nil case uint64: - return z.SetUint64(c1) + return z.SetUint64(c1), nil + case int8: + return z.SetInt64(int64(c1)), nil + case int16: + return z.SetInt64(int64(c1)), nil + case int32: + return z.SetInt64(int64(c1)), nil + case int64: + return z.SetInt64(c1), nil case int: - return z.SetString(strconv.Itoa(c1)) + return z.SetInt64(int64(c1)), nil case string: - return z.SetString(c1) + return z.SetString(c1), nil case *big.Int: - return z.SetBigInt(c1) + return z.SetBigInt(c1), nil case big.Int: - return z.SetBigInt(&c1) + return z.SetBigInt(&c1), nil case []byte: - return z.SetBytes(c1) + return z.SetBytes(c1), nil default: - panic("invalid type") + return nil, errors.New("can't set fr.Element from type " + reflect.TypeOf(i1).String()) } } @@ -152,6 +207,16 @@ func (z *Element) Div(x, y *Element) *Element { return z } +// Bit returns the i'th bit, with lsb == bit 0. +// It is the responsability of the caller to convert from Montgomery to Regular form if needed +func (z *Element) Bit(i uint64) uint64 { + j := i / 64 + if j >= 4 { + return 0 + } + return uint64(z[j] >> (i % 64) & 1) +} + // Equal returns z == x func (z *Element) Equal(x *Element) bool { return (z[3] == x[3]) && (z[2] == x[2]) && (z[1] == x[1]) && (z[0] == x[0]) @@ -162,6 +227,11 @@ func (z *Element) IsZero() bool { return (z[3] | z[2] | z[1] | z[0]) == 0 } +// IsUint64 reports whether z can be represented as an uint64. +func (z *Element) IsUint64() bool { + return (z[3] | z[2] | z[1]) == 0 +} + // Cmp compares (lexicographic order) z and x and returns: // // -1 if z < x @@ -227,7 +297,7 @@ func (z *Element) SetRandom() (*Element, error) { z[3] = binary.BigEndian.Uint64(bytes[24:32]) z[3] %= 3486998266802970665 - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -247,35 +317,39 @@ func One() Element { return one } -// MulAssign is deprecated -// Deprecated: use Mul instead -func (z *Element) MulAssign(x *Element) *Element { - return z.Mul(z, x) -} +// Halve sets z to z / 2 (mod p) +func (z *Element) Halve() { + if z[0]&1 == 1 { + var carry uint64 -// AddAssign is deprecated -// Deprecated: use Add instead -func (z *Element) AddAssign(x *Element) *Element { - return z.Add(z, x) -} + // z = z + q + z[0], carry = bits.Add64(z[0], 4891460686036598785, 0) + z[1], carry = bits.Add64(z[1], 2896914383306846353, carry) + z[2], carry = bits.Add64(z[2], 13281191951274694749, carry) + z[3], _ = bits.Add64(z[3], 3486998266802970665, carry) + + } + + // z = z >> 1 + + z[0] = z[0]>>1 | z[1]<<63 + z[1] = z[1]>>1 | z[2]<<63 + z[2] = z[2]>>1 | z[3]<<63 + z[3] >>= 1 -// SubAssign is deprecated -// Deprecated: use Sub instead -func (z *Element) SubAssign(x *Element) *Element { - return z.Sub(z, x) } // API with assembly impl // Mul z = x * y mod q -// see https://hackmd.io/@zkteam/modular_multiplication +// see https://hackmd.io/@gnark/modular_multiplication func (z *Element) Mul(x, y *Element) *Element { mul(z, x, y) return z } // Square z = x * x mod q -// see https://hackmd.io/@zkteam/modular_multiplication +// see https://hackmd.io/@gnark/modular_multiplication func (z *Element) Square(x *Element) *Element { mul(z, x, x) return z @@ -371,7 +445,58 @@ func _mulGeneric(z, x, y *Element) { z[3], z[2] = madd3(m, 3486998266802970665, c[0], c[2], c[1]) } - // if z > q --> z -= q + // if z > q → z -= q + // note: this is NOT constant time + if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 4891460686036598785, 0) + z[1], b = bits.Sub64(z[1], 2896914383306846353, b) + z[2], b = bits.Sub64(z[2], 13281191951274694749, b) + z[3], _ = bits.Sub64(z[3], 3486998266802970665, b) + } +} + +func _mulWGeneric(z, x *Element, y uint64) { + + var t [4]uint64 + { + // round 0 + c1, c0 := bits.Mul64(y, x[0]) + m := c0 * 14042775128853446655 + c2 := madd0(m, 4891460686036598785, c0) + c1, c0 = madd1(y, x[1], c1) + c2, t[0] = madd2(m, 2896914383306846353, c2, c0) + c1, c0 = madd1(y, x[2], c1) + c2, t[1] = madd2(m, 13281191951274694749, c2, c0) + c1, c0 = madd1(y, x[3], c1) + t[3], t[2] = madd3(m, 3486998266802970665, c0, c2, c1) + } + { + // round 1 + m := t[0] * 14042775128853446655 + c2 := madd0(m, 4891460686036598785, t[0]) + c2, t[0] = madd2(m, 2896914383306846353, c2, t[1]) + c2, t[1] = madd2(m, 13281191951274694749, c2, t[2]) + t[3], t[2] = madd2(m, 3486998266802970665, t[3], c2) + } + { + // round 2 + m := t[0] * 14042775128853446655 + c2 := madd0(m, 4891460686036598785, t[0]) + c2, t[0] = madd2(m, 2896914383306846353, c2, t[1]) + c2, t[1] = madd2(m, 13281191951274694749, c2, t[2]) + t[3], t[2] = madd2(m, 3486998266802970665, t[3], c2) + } + { + // round 3 + m := t[0] * 14042775128853446655 + c2 := madd0(m, 4891460686036598785, t[0]) + c2, z[0] = madd2(m, 2896914383306846353, c2, t[1]) + c2, z[1] = madd2(m, 13281191951274694749, c2, t[2]) + z[3], z[2] = madd2(m, 3486998266802970665, t[3], c2) + } + + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -422,7 +547,7 @@ func _fromMontGeneric(z *Element) { z[3] = C } - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -441,7 +566,7 @@ func _addGeneric(z, x, y *Element) { z[2], carry = bits.Add64(x[2], y[2], carry) z[3], _ = bits.Add64(x[3], y[3], carry) - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -460,7 +585,7 @@ func _doubleGeneric(z, x *Element) { z[2], carry = bits.Add64(x[2], x[2], carry) z[3], _ = bits.Add64(x[3], x[3], carry) - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -500,7 +625,7 @@ func _negGeneric(z, x *Element) { func _reduceGeneric(z *Element) { - // if z > q --> z -= q + // if z > q → z -= q // note: this is NOT constant time if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { var b uint64 @@ -528,10 +653,66 @@ func mulByConstant(z *Element, c uint8) { _z := *z z.Double(z).Double(z).Add(z, &_z) default: - panic("not implemented") + var y Element + y.SetUint64(uint64(c)) + z.Mul(z, &y) } } +// BatchInvert returns a new slice with every element inverted. +// Uses Montgomery batch inversion trick +func BatchInvert(a []Element) []Element { + res := make([]Element, len(a)) + if len(a) == 0 { + return res + } + + zeroes := make([]bool, len(a)) + accumulator := One() + + for i := 0; i < len(a); i++ { + if a[i].IsZero() { + zeroes[i] = true + continue + } + res[i] = accumulator + accumulator.Mul(&accumulator, &a[i]) + } + + accumulator.Inverse(&accumulator) + + for i := len(a) - 1; i >= 0; i-- { + if zeroes[i] { + continue + } + res[i].Mul(&res[i], &accumulator) + accumulator.Mul(&accumulator, &a[i]) + } + + return res +} + +func _butterflyGeneric(a, b *Element) { + t := *a + a.Add(a, b) + b.Sub(&t, b) +} + +// BitLen returns the minimum number of bits needed to represent z +// returns 0 if z == 0 +func (z *Element) BitLen() int { + if z[3] != 0 { + return 192 + bits.Len64(z[3]) + } + if z[2] != 0 { + return 128 + bits.Len64(z[2]) + } + if z[1] != 0 { + return 64 + bits.Len64(z[1]) + } + return bits.Len64(z[0]) +} + // Exp z = x^exponent mod q func (z *Element) Exp(x Element, exponent *big.Int) *Element { var bZero big.Int @@ -552,7 +733,7 @@ func (z *Element) Exp(x Element, exponent *big.Int) *Element { } // ToMont converts z to Montgomery form -// sets and returns z = z * r^2 +// sets and returns z = z * r² func (z *Element) ToMont() *Element { return z.Mul(z, &rSquare) } @@ -562,11 +743,41 @@ func (z Element) ToRegular() Element { return *z.FromMont() } -// String returns the string form of an Element in Montgomery form +// String returns the decimal representation of z as generated by +// z.Text(10). func (z *Element) String() string { + return z.Text(10) +} + +// Text returns the string representation of z in the given base. +// Base must be between 2 and 36, inclusive. The result uses the +// lower-case letters 'a' to 'z' for digit values 10 to 35. +// No prefix (such as "0x") is added to the string. If z is a nil +// pointer it returns "". +// If base == 10 and -z fits in a uint64 prefix "-" is added to the string. +func (z *Element) Text(base int) string { + if base < 2 || base > 36 { + panic("invalid base") + } + if z == nil { + return "" + } + zz := *z + zz.FromMont() + if zz.IsUint64() { + return strconv.FormatUint(zz[0], base) + } else if base == 10 { + var zzNeg Element + zzNeg.Neg(z) + zzNeg.FromMont() + if zzNeg.IsUint64() { + return "-" + strconv.FormatUint(zzNeg[0], base) + } + } vv := bigIntPool.Get().(*big.Int) - defer bigIntPool.Put(vv) - return z.ToBigIntRegular(vv).String() + r := zz.ToBigInt(vv).Text(base) + bigIntPool.Put(vv) + return r } // ToBigInt returns z as a big.Int in Montgomery form @@ -598,6 +809,13 @@ func (z *Element) Bytes() (res [Limbs * 8]byte) { return } +// Marshal returns the regular (non montgomery) value +// of z as a big-endian byte slice. +func (z *Element) Marshal() []byte { + b := z.Bytes() + return b[:] +} + // SetBytes interprets e as the bytes of a big-endian unsigned integer, // sets z to that value (in Montgomery form), and returns z. func (z *Element) SetBytes(e []byte) *Element { @@ -645,7 +863,7 @@ func (z *Element) SetBigInt(v *big.Int) *Element { return z } -// setBigInt assumes 0 <= v < q +// setBigInt assumes 0 ⩽ v < q func (z *Element) setBigInt(v *big.Int) *Element { vBits := v.Bits() @@ -666,14 +884,30 @@ func (z *Element) setBigInt(v *big.Int) *Element { return z.ToMont() } -// SetString creates a big.Int with s (in base 10) and calls SetBigInt on z -func (z *Element) SetString(s string) *Element { +// SetString creates a big.Int with number and calls SetBigInt on z +// +// The number prefix determines the actual base: A prefix of +// ''0b'' or ''0B'' selects base 2, ''0'', ''0o'' or ''0O'' selects base 8, +// and ''0x'' or ''0X'' selects base 16. Otherwise, the selected base is 10 +// and no prefix is accepted. +// +// For base 16, lower and upper case letters are considered the same: +// The letters 'a' to 'f' and 'A' to 'F' represent digit values 10 to 15. +// +// An underscore character ''_'' may appear between a base +// prefix and an adjacent digit, and between successive digits; such +// underscores do not change the value of the number. +// Incorrect placement of underscores is reported as a panic if there +// are no other errors. +// +func (z *Element) SetString(number string) *Element { // get temporary big int from the pool vv := bigIntPool.Get().(*big.Int) - if _, ok := vv.SetString(s, 10); !ok { - panic("Element.SetString failed -> can't parse number in base10 into a big.Int") + if _, ok := vv.SetString(number, 0); !ok { + panic("Element.SetString failed -> can't parse number into a big.Int " + number) } + z.SetBigInt(vv) // release object into pool @@ -682,22 +916,59 @@ func (z *Element) SetString(s string) *Element { return z } -var ( - _bLegendreExponentElement *big.Int - _bSqrtExponentElement *big.Int -) +// MarshalJSON returns json encoding of z (z.Text(10)) +// If z == nil, returns null +func (z *Element) MarshalJSON() ([]byte, error) { + if z == nil { + return []byte("null"), nil + } + const maxSafeBound = 15 // we encode it as number if it's small + s := z.Text(10) + if len(s) <= maxSafeBound { + return []byte(s), nil + } + var sbb strings.Builder + sbb.WriteByte('"') + sbb.WriteString(s) + sbb.WriteByte('"') + return []byte(sbb.String()), nil +} -func init() { - _bLegendreExponentElement, _ = new(big.Int).SetString("183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000", 16) - const sqrtExponentElement = "183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f" - _bSqrtExponentElement, _ = new(big.Int).SetString(sqrtExponentElement, 16) +// UnmarshalJSON accepts numbers and strings as input +// See Element.SetString for valid prefixes (0x, 0b, ...) +func (z *Element) UnmarshalJSON(data []byte) error { + s := string(data) + if len(s) > Bits*3 { + return errors.New("value too large (max = Element.Bits * 3)") + } + + // we accept numbers and strings, remove leading and trailing quotes if any + if len(s) > 0 && s[0] == '"' { + s = s[1:] + } + if len(s) > 0 && s[len(s)-1] == '"' { + s = s[:len(s)-1] + } + + // get temporary big int from the pool + vv := bigIntPool.Get().(*big.Int) + + if _, ok := vv.SetString(s, 0); !ok { + return errors.New("can't parse into a big.Int: " + s) + } + + z.SetBigInt(vv) + + // release object into pool + bigIntPool.Put(vv) + return nil } // Legendre returns the Legendre symbol of z (either +1, -1, or 0.) func (z *Element) Legendre() int { var l Element // z^((q-1)/2) - l.Exp(*z, _bLegendreExponentElement) + l.expByLegendreExp(*z) if l.IsZero() { return 0 @@ -720,7 +991,7 @@ func (z *Element) Sqrt(x *Element) *Element { var y, b, t, w Element // w = x^((s-1)/2)) - w.Exp(*x, _bSqrtExponentElement) + w.expBySqrtExp(*x) // y = x^((s+1)/2)) = w * x y.Mul(x, &w) @@ -778,170 +1049,418 @@ func (z *Element) Sqrt(x *Element) *Element { } } -// Inverse z = x^-1 mod q -// Algorithm 16 in "Efficient Software-Implementation of Finite Fields with Applications to Cryptography" -// if x == 0, sets and returns z = x -func (z *Element) Inverse(x *Element) *Element { - if x.IsZero() { - return z.Set(x) +func max(a int, b int) int { + if a > b { + return a } + return b +} - // initialize u = q - var u = Element{ - 4891460686036598785, - 2896914383306846353, - 13281191951274694749, - 3486998266802970665, +func min(a int, b int) int { + if a < b { + return a } + return b +} - // initialize s = r^2 - var s = Element{ - 1997599621687373223, - 6052339484930628067, - 10108755138030829701, - 150537098327114917, +const updateFactorsConversionBias int64 = 0x7fffffff7fffffff // (2³¹ - 1)(2³² + 1) +const updateFactorIdentityMatrixRow0 = 1 +const updateFactorIdentityMatrixRow1 = 1 << 32 + +func updateFactorsDecompose(c int64) (int64, int64) { + c += updateFactorsConversionBias + const low32BitsFilter int64 = 0xFFFFFFFF + f := c&low32BitsFilter - 0x7FFFFFFF + g := c>>32&low32BitsFilter - 0x7FFFFFFF + return f, g +} + +const k = 32 // word size / 2 +const signBitSelector = uint64(1) << 63 +const approxLowBitsN = k - 1 +const approxHighBitsN = k + 1 +const inversionCorrectionFactorWord0 = 13488105295233737379 +const inversionCorrectionFactorWord1 = 17373395488625725466 +const inversionCorrectionFactorWord2 = 6831692495576925776 +const inversionCorrectionFactorWord3 = 3282329835997625403 + +const invIterationsN = 18 + +// Inverse z = x⁻¹ mod q +// Implements "Optimized Binary GCD for Modular Inversion" +// https://github.com/pornin/bingcd/blob/main/doc/bingcd.pdf +func (z *Element) Inverse(x *Element) *Element { + if x.IsZero() { + z.SetZero() + return z } - // r = 0 - r := Element{} + a := *x + b := Element{ + qElementWord0, + qElementWord1, + qElementWord2, + qElementWord3, + } // b := q - v := *x + u := Element{1} - var carry, borrow, t, t2 uint64 - var bigger bool + // Update factors: we get [u; v]:= [f0 g0; f1 g1] [u; v] + // c_i = f_i + 2³¹ - 1 + 2³² * (g_i + 2³¹ - 1) + var c0, c1 int64 - for { - for v[0]&1 == 0 { - - // v = v >> 1 - t2 = v[3] << 63 - v[3] >>= 1 - t = t2 - t2 = v[2] << 63 - v[2] = (v[2] >> 1) | t - t = t2 - t2 = v[1] << 63 - v[1] = (v[1] >> 1) | t - t = t2 - v[0] = (v[0] >> 1) | t - - if s[0]&1 == 1 { - - // s = s + q - s[0], carry = bits.Add64(s[0], 4891460686036598785, 0) - s[1], carry = bits.Add64(s[1], 2896914383306846353, carry) - s[2], carry = bits.Add64(s[2], 13281191951274694749, carry) - s[3], _ = bits.Add64(s[3], 3486998266802970665, carry) + // Saved update factors to reduce the number of field multiplications + var pf0, pf1, pg0, pg1 int64 + + var i uint + + var v, s Element + // Since u,v are updated every other iteration, we must make sure we terminate after evenly many iterations + // This also lets us get away with half as many updates to u,v + // To make this constant-time-ish, replace the condition with i < invIterationsN + for i = 0; i&1 == 1 || !a.IsZero(); i++ { + n := max(a.BitLen(), b.BitLen()) + aApprox, bApprox := approximate(&a, n), approximate(&b, n) + + // After 0 iterations, we have f₀ ≤ 2⁰ and f₁ < 2⁰ + // f0, g0, f1, g1 = 1, 0, 0, 1 + c0, c1 = updateFactorIdentityMatrixRow0, updateFactorIdentityMatrixRow1 + + for j := 0; j < approxLowBitsN; j++ { + + if aApprox&1 == 0 { + aApprox /= 2 + } else { + s, borrow := bits.Sub64(aApprox, bApprox, 0) + if borrow == 1 { + s = bApprox - aApprox + bApprox = aApprox + c0, c1 = c1, c0 + } + + aApprox = s / 2 + c0 = c0 - c1 + + // Now |f₀| < 2ʲ + 2ʲ = 2ʲ⁺¹ + // |f₁| ≤ 2ʲ still } - // s = s >> 1 - t2 = s[3] << 63 - s[3] >>= 1 - t = t2 - t2 = s[2] << 63 - s[2] = (s[2] >> 1) | t - t = t2 - t2 = s[1] << 63 - s[1] = (s[1] >> 1) | t - t = t2 - s[0] = (s[0] >> 1) | t + c1 *= 2 + // |f₁| ≤ 2ʲ⁺¹ + } + + s = a + var g0 int64 + // from this point on c0 aliases for f0 + c0, g0 = updateFactorsDecompose(c0) + aHi := a.linearCombNonModular(&s, c0, &b, g0) + if aHi&signBitSelector != 0 { + // if aHi < 0 + c0, g0 = -c0, -g0 + aHi = a.neg(&a, aHi) } - for u[0]&1 == 0 { - - // u = u >> 1 - t2 = u[3] << 63 - u[3] >>= 1 - t = t2 - t2 = u[2] << 63 - u[2] = (u[2] >> 1) | t - t = t2 - t2 = u[1] << 63 - u[1] = (u[1] >> 1) | t - t = t2 - u[0] = (u[0] >> 1) | t - - if r[0]&1 == 1 { - - // r = r + q - r[0], carry = bits.Add64(r[0], 4891460686036598785, 0) - r[1], carry = bits.Add64(r[1], 2896914383306846353, carry) - r[2], carry = bits.Add64(r[2], 13281191951274694749, carry) - r[3], _ = bits.Add64(r[3], 3486998266802970665, carry) + // right-shift a by k-1 bits + a[0] = (a[0] >> approxLowBitsN) | ((a[1]) << approxHighBitsN) + a[1] = (a[1] >> approxLowBitsN) | ((a[2]) << approxHighBitsN) + a[2] = (a[2] >> approxLowBitsN) | ((a[3]) << approxHighBitsN) + a[3] = (a[3] >> approxLowBitsN) | (aHi << approxHighBitsN) + + var f1 int64 + // from this point on c1 aliases for g0 + f1, c1 = updateFactorsDecompose(c1) + bHi := b.linearCombNonModular(&s, f1, &b, c1) + if bHi&signBitSelector != 0 { + // if bHi < 0 + f1, c1 = -f1, -c1 + bHi = b.neg(&b, bHi) + } + // right-shift b by k-1 bits + b[0] = (b[0] >> approxLowBitsN) | ((b[1]) << approxHighBitsN) + b[1] = (b[1] >> approxLowBitsN) | ((b[2]) << approxHighBitsN) + b[2] = (b[2] >> approxLowBitsN) | ((b[3]) << approxHighBitsN) + b[3] = (b[3] >> approxLowBitsN) | (bHi << approxHighBitsN) + + if i&1 == 1 { + // Combine current update factors with previously stored ones + // [f₀, g₀; f₁, g₁] ← [f₀, g₀; f₁, g₀] [pf₀, pg₀; pf₀, pg₀] + // We have |f₀|, |g₀|, |pf₀|, |pf₁| ≤ 2ᵏ⁻¹, and that |pf_i| < 2ᵏ⁻¹ for i ∈ {0, 1} + // Then for the new value we get |f₀| < 2ᵏ⁻¹ × 2ᵏ⁻¹ + 2ᵏ⁻¹ × 2ᵏ⁻¹ = 2²ᵏ⁻¹ + // Which leaves us with an extra bit for the sign + + // c0 aliases f0, c1 aliases g1 + c0, g0, f1, c1 = c0*pf0+g0*pf1, + c0*pg0+g0*pg1, + f1*pf0+c1*pf1, + f1*pg0+c1*pg1 + + s = u + u.linearCombSosSigned(&u, c0, &v, g0) + v.linearCombSosSigned(&s, f1, &v, c1) - } + } else { + // Save update factors + pf0, pg0, pf1, pg1 = c0, g0, f1, c1 + } + } + + // For every iteration that we miss, v is not being multiplied by 2²ᵏ⁻² + const pSq int64 = 1 << (2 * (k - 1)) + // If the function is constant-time ish, this loop will not run (probably no need to take it out explicitly) + for ; i < invIterationsN; i += 2 { + v.mulWSigned(&v, pSq) + } + + z.Mul(&v, &Element{ + inversionCorrectionFactorWord0, + inversionCorrectionFactorWord1, + inversionCorrectionFactorWord2, + inversionCorrectionFactorWord3, + }) + return z +} + +// approximate a big number x into a single 64 bit word using its uppermost and lowermost bits +// if x fits in a word as is, no approximation necessary +func approximate(x *Element, nBits int) uint64 { + + if nBits <= 64 { + return x[0] + } + + const mask = (uint64(1) << (k - 1)) - 1 // k-1 ones + lo := mask & x[0] + + hiWordIndex := (nBits - 1) / 64 + + hiWordBitsAvailable := nBits - hiWordIndex*64 + hiWordBitsUsed := min(hiWordBitsAvailable, approxHighBitsN) + + mask_ := uint64(^((1 << (hiWordBitsAvailable - hiWordBitsUsed)) - 1)) + hi := (x[hiWordIndex] & mask_) << (64 - hiWordBitsAvailable) + + mask_ = ^(1<<(approxLowBitsN+hiWordBitsUsed) - 1) + mid := (mask_ & x[hiWordIndex-1]) >> hiWordBitsUsed + + return lo | mid | hi +} + +func (z *Element) linearCombSosSigned(x *Element, xC int64, y *Element, yC int64) { + hi := z.linearCombNonModular(x, xC, y, yC) + z.montReduceSigned(z, hi) +} + +// montReduceSigned SOS algorithm; xHi must be at most 63 bits long. Last bit of xHi may be used as a sign bit +func (z *Element) montReduceSigned(x *Element, xHi uint64) { + + const signBitRemover = ^signBitSelector + neg := xHi&signBitSelector != 0 + // the SOS implementation requires that most significant bit is 0 + // Let X be xHi*r + x + // note that if X is negative we would have initially stored it as 2⁶⁴ r + X + xHi &= signBitRemover + // with this a negative X is now represented as 2⁶³ r + X + + var t [2*Limbs - 1]uint64 + var C uint64 - // r = r >> 1 - t2 = r[3] << 63 - r[3] >>= 1 - t = t2 - t2 = r[2] << 63 - r[2] = (r[2] >> 1) | t - t = t2 - t2 = r[1] << 63 - r[1] = (r[1] >> 1) | t - t = t2 - r[0] = (r[0] >> 1) | t + m := x[0] * qInvNegLsw + C = madd0(m, qElementWord0, x[0]) + C, t[1] = madd2(m, qElementWord1, x[1], C) + C, t[2] = madd2(m, qElementWord2, x[2], C) + C, t[3] = madd2(m, qElementWord3, x[3], C) + + // the high word of m * qElement[3] is at most 62 bits + // x[3] + C is at most 65 bits (high word at most 1 bit) + // Thus the resulting C will be at most 63 bits + t[4] = xHi + C + // xHi and C are 63 bits, therefore no overflow + + { + const i = 1 + m = t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, t[i+1] = madd2(m, qElementWord1, t[i+1], C) + C, t[i+2] = madd2(m, qElementWord2, t[i+2], C) + C, t[i+3] = madd2(m, qElementWord3, t[i+3], C) + + t[i+Limbs] += C + } + { + const i = 2 + m = t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, t[i+1] = madd2(m, qElementWord1, t[i+1], C) + C, t[i+2] = madd2(m, qElementWord2, t[i+2], C) + C, t[i+3] = madd2(m, qElementWord3, t[i+3], C) + + t[i+Limbs] += C + } + { + const i = 3 + m := t[i] * qInvNegLsw + + C = madd0(m, qElementWord0, t[i+0]) + C, z[0] = madd2(m, qElementWord1, t[i+1], C) + C, z[1] = madd2(m, qElementWord2, t[i+2], C) + z[3], z[2] = madd2(m, qElementWord3, t[i+3], C) + } + + // if z > q → z -= q + // note: this is NOT constant time + if !(z[3] < 3486998266802970665 || (z[3] == 3486998266802970665 && (z[2] < 13281191951274694749 || (z[2] == 13281191951274694749 && (z[1] < 2896914383306846353 || (z[1] == 2896914383306846353 && (z[0] < 4891460686036598785))))))) { + var b uint64 + z[0], b = bits.Sub64(z[0], 4891460686036598785, 0) + z[1], b = bits.Sub64(z[1], 2896914383306846353, b) + z[2], b = bits.Sub64(z[2], 13281191951274694749, b) + z[3], _ = bits.Sub64(z[3], 3486998266802970665, b) + } + if neg { + // We have computed ( 2⁶³ r + X ) r⁻¹ = 2⁶³ + X r⁻¹ instead + var b uint64 + z[0], b = bits.Sub64(z[0], signBitSelector, 0) + z[1], b = bits.Sub64(z[1], 0, b) + z[2], b = bits.Sub64(z[2], 0, b) + z[3], b = bits.Sub64(z[3], 0, b) + + // Occurs iff x == 0 && xHi < 0, i.e. X = rX' for -2⁶³ ≤ X' < 0 + if b != 0 { + // z[3] = -1 + // negative: add q + const neg1 = 0xFFFFFFFFFFFFFFFF + + b = 0 + z[0], b = bits.Add64(z[0], qElementWord0, b) + z[1], b = bits.Add64(z[1], qElementWord1, b) + z[2], b = bits.Add64(z[2], qElementWord2, b) + z[3], _ = bits.Add64(neg1, qElementWord3, b) } + } +} - // v >= u - bigger = !(v[3] < u[3] || (v[3] == u[3] && (v[2] < u[2] || (v[2] == u[2] && (v[1] < u[1] || (v[1] == u[1] && (v[0] < u[0]))))))) +// mulWSigned mul word signed (w/ montgomery reduction) +func (z *Element) mulWSigned(x *Element, y int64) { + m := y >> 63 + _mulWGeneric(z, x, uint64((y^m)-m)) + // multiply by abs(y) + if y < 0 { + z.Neg(z) + } +} - if bigger { +func (z *Element) neg(x *Element, xHi uint64) uint64 { + var b uint64 - // v = v - u - v[0], borrow = bits.Sub64(v[0], u[0], 0) - v[1], borrow = bits.Sub64(v[1], u[1], borrow) - v[2], borrow = bits.Sub64(v[2], u[2], borrow) - v[3], _ = bits.Sub64(v[3], u[3], borrow) + z[0], b = bits.Sub64(0, x[0], 0) + z[1], b = bits.Sub64(0, x[1], b) + z[2], b = bits.Sub64(0, x[2], b) + z[3], b = bits.Sub64(0, x[3], b) + xHi, _ = bits.Sub64(0, xHi, b) - // s = s - r - s[0], borrow = bits.Sub64(s[0], r[0], 0) - s[1], borrow = bits.Sub64(s[1], r[1], borrow) - s[2], borrow = bits.Sub64(s[2], r[2], borrow) - s[3], borrow = bits.Sub64(s[3], r[3], borrow) + return xHi +} - if borrow == 1 { +// regular multiplication by one word regular (non montgomery) +// Fewer additions than the branch-free for positive y. Could be faster on some architectures +func (z *Element) mulWRegular(x *Element, y int64) uint64 { - // s = s + q - s[0], carry = bits.Add64(s[0], 4891460686036598785, 0) - s[1], carry = bits.Add64(s[1], 2896914383306846353, carry) - s[2], carry = bits.Add64(s[2], 13281191951274694749, carry) - s[3], _ = bits.Add64(s[3], 3486998266802970665, carry) + // w := abs(y) + m := y >> 63 + w := uint64((y ^ m) - m) - } - } else { + var c uint64 + c, z[0] = bits.Mul64(x[0], w) + c, z[1] = madd1(x[1], w, c) + c, z[2] = madd1(x[2], w, c) + c, z[3] = madd1(x[3], w, c) - // u = u - v - u[0], borrow = bits.Sub64(u[0], v[0], 0) - u[1], borrow = bits.Sub64(u[1], v[1], borrow) - u[2], borrow = bits.Sub64(u[2], v[2], borrow) - u[3], _ = bits.Sub64(u[3], v[3], borrow) + if y < 0 { + c = z.neg(z, c) + } - // r = r - s - r[0], borrow = bits.Sub64(r[0], s[0], 0) - r[1], borrow = bits.Sub64(r[1], s[1], borrow) - r[2], borrow = bits.Sub64(r[2], s[2], borrow) - r[3], borrow = bits.Sub64(r[3], s[3], borrow) + return c +} - if borrow == 1 { +/* +Removed: seems slower +// mulWRegular branch-free regular multiplication by one word (non montgomery) +func (z *Element) mulWRegularBf(x *Element, y int64) uint64 { - // r = r + q - r[0], carry = bits.Add64(r[0], 4891460686036598785, 0) - r[1], carry = bits.Add64(r[1], 2896914383306846353, carry) - r[2], carry = bits.Add64(r[2], 13281191951274694749, carry) - r[3], _ = bits.Add64(r[3], 3486998266802970665, carry) + w := uint64(y) + allNeg := uint64(y >> 63) // -1 if y < 0, 0 o.w - } + // s[0], s[1] so results are not stored immediately in z. + // x[i] will be needed in the i+1 th iteration. We don't want to overwrite it in case x = z + var s [2]uint64 + var h [2]uint64 + + h[0], s[0] = bits.Mul64(x[0], w) + + c := uint64(0) + b := uint64(0) + + { + const curI = 1 % 2 + const prevI = 1 - curI + const iMinusOne = 1 - 1 + + h[curI], s[curI] = bits.Mul64(x[1], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } - if (u[0] == 1) && (u[3]|u[2]|u[1]) == 0 { - return z.Set(&r) + + { + const curI = 2 % 2 + const prevI = 1 - curI + const iMinusOne = 2 - 1 + + h[curI], s[curI] = bits.Mul64(x[2], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } - if (v[0] == 1) && (v[3]|v[2]|v[1]) == 0 { - return z.Set(&s) + + { + const curI = 3 % 2 + const prevI = 1 - curI + const iMinusOne = 3 - 1 + + h[curI], s[curI] = bits.Mul64(x[3], w) + s[curI], c = bits.Add64(s[curI], h[prevI], c) + s[curI], b = bits.Sub64(s[curI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] } + { + const curI = 4 % 2 + const prevI = 1 - curI + const iMinusOne = 3 + + s[curI], _ = bits.Sub64(h[prevI], allNeg & x[iMinusOne], b) + z[iMinusOne] = s[prevI] + + return s[curI] + c } +}*/ + +// Requires NoCarry +func (z *Element) linearCombNonModular(x *Element, xC int64, y *Element, yC int64) uint64 { + var yTimes Element + + yHi := yTimes.mulWRegular(y, yC) + xHi := z.mulWRegular(x, xC) + + carry := uint64(0) + z[0], carry = bits.Add64(z[0], yTimes[0], carry) + z[1], carry = bits.Add64(z[1], yTimes[1], carry) + z[2], carry = bits.Add64(z[2], yTimes[2], carry) + z[3], carry = bits.Add64(z[3], yTimes[3], carry) + + yHi, _ = bits.Add64(xHi, yHi, carry) + return yHi } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_exp.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_exp.go new file mode 100644 index 00000000000..e8c4da79289 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_exp.go @@ -0,0 +1,819 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package fr + +// expBySqrtExp is equivalent to z.Exp(x, 183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f) +// +// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain +func (z *Element) expBySqrtExp(x Element) *Element { + // addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _111 = _10 + _101 + // _1001 = _10 + _111 + // _1011 = _10 + _1001 + // _1101 = _10 + _1011 + // _1111 = _10 + _1101 + // _11000 = _1001 + _1111 + // _11111 = _111 + _11000 + // i26 = ((_11000 << 4 + _11) << 3 + 1) << 7 + // i36 = ((_1001 + i26) << 2 + _11) << 5 + _111 + // i53 = (2*(i36 << 6 + _1011) + 1) << 8 + // i64 = (2*(_1001 + i53) + 1) << 7 + _1101 + // i84 = ((i64 << 10 + _101) << 6 + _1101) << 2 + // i100 = ((_11 + i84) << 7 + _101) << 6 + 1 + // i117 = ((i100 << 7 + _1011) << 5 + _1101) << 3 + // i137 = ((_101 + i117) << 8 + _11) << 9 + _101 + // i153 = ((i137 << 3 + _11) << 8 + _1011) << 3 + // i168 = ((_101 + i153) << 5 + _101) << 7 + _11 + // i187 = ((i168 << 7 + _11111) << 2 + 1) << 8 + // i204 = ((_1001 + i187) << 8 + _1111) << 6 + _1101 + // i215 = 2*((i204 << 2 + _11) << 6 + _1011) + // i232 = ((1 + i215) << 8 + _1001) << 6 + _101 + // i257 = ((i232 << 9 + _11111) << 9 + _11111) << 5 + // return ((_1011 + i257) << 3 + 1) << 7 + _11111 + // + // Operations: 221 squares 49 multiplies + + // Allocate Temporaries. + var ( + t0 = new(Element) + t1 = new(Element) + t2 = new(Element) + t3 = new(Element) + t4 = new(Element) + t5 = new(Element) + t6 = new(Element) + t7 = new(Element) + ) + + // var t0,t1,t2,t3,t4,t5,t6,t7 Element + // Step 1: z = x^0x2 + z.Square(&x) + + // Step 2: t3 = x^0x3 + t3.Mul(&x, z) + + // Step 3: t1 = x^0x5 + t1.Mul(z, t3) + + // Step 4: t6 = x^0x7 + t6.Mul(z, t1) + + // Step 5: t2 = x^0x9 + t2.Mul(z, t6) + + // Step 6: t0 = x^0xb + t0.Mul(z, t2) + + // Step 7: t4 = x^0xd + t4.Mul(z, t0) + + // Step 8: t5 = x^0xf + t5.Mul(z, t4) + + // Step 9: t7 = x^0x18 + t7.Mul(t2, t5) + + // Step 10: z = x^0x1f + z.Mul(t6, t7) + + // Step 14: t7 = x^0x180 + for s := 0; s < 4; s++ { + t7.Square(t7) + } + + // Step 15: t7 = x^0x183 + t7.Mul(t3, t7) + + // Step 18: t7 = x^0xc18 + for s := 0; s < 3; s++ { + t7.Square(t7) + } + + // Step 19: t7 = x^0xc19 + t7.Mul(&x, t7) + + // Step 26: t7 = x^0x60c80 + for s := 0; s < 7; s++ { + t7.Square(t7) + } + + // Step 27: t7 = x^0x60c89 + t7.Mul(t2, t7) + + // Step 29: t7 = x^0x183224 + for s := 0; s < 2; s++ { + t7.Square(t7) + } + + // Step 30: t7 = x^0x183227 + t7.Mul(t3, t7) + + // Step 35: t7 = x^0x30644e0 + for s := 0; s < 5; s++ { + t7.Square(t7) + } + + // Step 36: t6 = x^0x30644e7 + t6.Mul(t6, t7) + + // Step 42: t6 = x^0xc19139c0 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 43: t6 = x^0xc19139cb + t6.Mul(t0, t6) + + // Step 44: t6 = x^0x183227396 + t6.Square(t6) + + // Step 45: t6 = x^0x183227397 + t6.Mul(&x, t6) + + // Step 53: t6 = x^0x18322739700 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 54: t6 = x^0x18322739709 + t6.Mul(t2, t6) + + // Step 55: t6 = x^0x30644e72e12 + t6.Square(t6) + + // Step 56: t6 = x^0x30644e72e13 + t6.Mul(&x, t6) + + // Step 63: t6 = x^0x1832273970980 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 64: t6 = x^0x183227397098d + t6.Mul(t4, t6) + + // Step 74: t6 = x^0x60c89ce5c263400 + for s := 0; s < 10; s++ { + t6.Square(t6) + } + + // Step 75: t6 = x^0x60c89ce5c263405 + t6.Mul(t1, t6) + + // Step 81: t6 = x^0x183227397098d0140 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 82: t6 = x^0x183227397098d014d + t6.Mul(t4, t6) + + // Step 84: t6 = x^0x60c89ce5c26340534 + for s := 0; s < 2; s++ { + t6.Square(t6) + } + + // Step 85: t6 = x^0x60c89ce5c26340537 + t6.Mul(t3, t6) + + // Step 92: t6 = x^0x30644e72e131a029b80 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 93: t6 = x^0x30644e72e131a029b85 + t6.Mul(t1, t6) + + // Step 99: t6 = x^0xc19139cb84c680a6e140 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 100: t6 = x^0xc19139cb84c680a6e141 + t6.Mul(&x, t6) + + // Step 107: t6 = x^0x60c89ce5c263405370a080 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 108: t6 = x^0x60c89ce5c263405370a08b + t6.Mul(t0, t6) + + // Step 113: t6 = x^0xc19139cb84c680a6e141160 + for s := 0; s < 5; s++ { + t6.Square(t6) + } + + // Step 114: t6 = x^0xc19139cb84c680a6e14116d + t6.Mul(t4, t6) + + // Step 117: t6 = x^0x60c89ce5c263405370a08b68 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 118: t6 = x^0x60c89ce5c263405370a08b6d + t6.Mul(t1, t6) + + // Step 126: t6 = x^0x60c89ce5c263405370a08b6d00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 127: t6 = x^0x60c89ce5c263405370a08b6d03 + t6.Mul(t3, t6) + + // Step 136: t6 = x^0xc19139cb84c680a6e14116da0600 + for s := 0; s < 9; s++ { + t6.Square(t6) + } + + // Step 137: t6 = x^0xc19139cb84c680a6e14116da0605 + t6.Mul(t1, t6) + + // Step 140: t6 = x^0x60c89ce5c263405370a08b6d03028 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 141: t6 = x^0x60c89ce5c263405370a08b6d0302b + t6.Mul(t3, t6) + + // Step 149: t6 = x^0x60c89ce5c263405370a08b6d0302b00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 150: t6 = x^0x60c89ce5c263405370a08b6d0302b0b + t6.Mul(t0, t6) + + // Step 153: t6 = x^0x30644e72e131a029b85045b681815858 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 154: t6 = x^0x30644e72e131a029b85045b68181585d + t6.Mul(t1, t6) + + // Step 159: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba0 + for s := 0; s < 5; s++ { + t6.Square(t6) + } + + // Step 160: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5 + t6.Mul(t1, t6) + + // Step 167: t6 = x^0x30644e72e131a029b85045b68181585d280 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 168: t6 = x^0x30644e72e131a029b85045b68181585d283 + t6.Mul(t3, t6) + + // Step 175: t6 = x^0x183227397098d014dc2822db40c0ac2e94180 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 176: t6 = x^0x183227397098d014dc2822db40c0ac2e9419f + t6.Mul(z, t6) + + // Step 178: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067c + for s := 0; s < 2; s++ { + t6.Square(t6) + } + + // Step 179: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d + t6.Mul(&x, t6) + + // Step 187: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 188: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d09 + t6.Mul(t2, t6) + + // Step 196: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d0900 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 197: t5 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f + t5.Mul(t5, t6) + + // Step 203: t5 = x^0x183227397098d014dc2822db40c0ac2e9419f4243c0 + for s := 0; s < 6; s++ { + t5.Square(t5) + } + + // Step 204: t4 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cd + t4.Mul(t4, t5) + + // Step 206: t4 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f34 + for s := 0; s < 2; s++ { + t4.Square(t4) + } + + // Step 207: t3 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f37 + t3.Mul(t3, t4) + + // Step 213: t3 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdc0 + for s := 0; s < 6; s++ { + t3.Square(t3) + } + + // Step 214: t3 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb + t3.Mul(t0, t3) + + // Step 215: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b96 + t3.Square(t3) + + // Step 216: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b97 + t3.Mul(&x, t3) + + // Step 224: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b9700 + for s := 0; s < 8; s++ { + t3.Square(t3) + } + + // Step 225: t2 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709 + t2.Mul(t2, t3) + + // Step 231: t2 = x^0xc19139cb84c680a6e14116da06056174a0cfa121e6e5c240 + for s := 0; s < 6; s++ { + t2.Square(t2) + } + + // Step 232: t1 = x^0xc19139cb84c680a6e14116da06056174a0cfa121e6e5c245 + t1.Mul(t1, t2) + + // Step 241: t1 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a00 + for s := 0; s < 9; s++ { + t1.Square(t1) + } + + // Step 242: t1 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f + t1.Mul(z, t1) + + // Step 251: t1 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e00 + for s := 0; s < 9; s++ { + t1.Square(t1) + } + + // Step 252: t1 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f + t1.Mul(z, t1) + + // Step 257: t1 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3e0 + for s := 0; s < 5; s++ { + t1.Square(t1) + } + + // Step 258: t0 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3eb + t0.Mul(t0, t1) + + // Step 261: t0 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f58 + for s := 0; s < 3; s++ { + t0.Square(t0) + } + + // Step 262: t0 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f59 + t0.Mul(&x, t0) + + // Step 269: t0 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac80 + for s := 0; s < 7; s++ { + t0.Square(t0) + } + + // Step 270: z = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f + z.Mul(z, t0) + + return z +} + +// expByLegendreExp is equivalent to z.Exp(x, 183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000) +// +// uses github.com/mmcloughlin/addchain v0.4.0 to generate a shorter addition chain +func (z *Element) expByLegendreExp(x Element) *Element { + // addition chain: + // + // _10 = 2*1 + // _11 = 1 + _10 + // _101 = _10 + _11 + // _111 = _10 + _101 + // _1001 = _10 + _111 + // _1011 = _10 + _1001 + // _1101 = _10 + _1011 + // _1111 = _10 + _1101 + // _11000 = _1001 + _1111 + // _11111 = _111 + _11000 + // i26 = ((_11000 << 4 + _11) << 3 + 1) << 7 + // i36 = ((_1001 + i26) << 2 + _11) << 5 + _111 + // i53 = (2*(i36 << 6 + _1011) + 1) << 8 + // i64 = (2*(_1001 + i53) + 1) << 7 + _1101 + // i84 = ((i64 << 10 + _101) << 6 + _1101) << 2 + // i100 = ((_11 + i84) << 7 + _101) << 6 + 1 + // i117 = ((i100 << 7 + _1011) << 5 + _1101) << 3 + // i137 = ((_101 + i117) << 8 + _11) << 9 + _101 + // i153 = ((i137 << 3 + _11) << 8 + _1011) << 3 + // i168 = ((_101 + i153) << 5 + _101) << 7 + _11 + // i187 = ((i168 << 7 + _11111) << 2 + 1) << 8 + // i204 = ((_1001 + i187) << 8 + _1111) << 6 + _1101 + // i215 = 2*((i204 << 2 + _11) << 6 + _1011) + // i232 = ((1 + i215) << 8 + _1001) << 6 + _101 + // i257 = ((i232 << 9 + _11111) << 9 + _11111) << 5 + // i270 = ((_1011 + i257) << 3 + 1) << 7 + _11111 + // return (2*i270 + 1) << 27 + // + // Operations: 249 squares 50 multiplies + + // Allocate Temporaries. + var ( + t0 = new(Element) + t1 = new(Element) + t2 = new(Element) + t3 = new(Element) + t4 = new(Element) + t5 = new(Element) + t6 = new(Element) + t7 = new(Element) + ) + + // var t0,t1,t2,t3,t4,t5,t6,t7 Element + // Step 1: z = x^0x2 + z.Square(&x) + + // Step 2: t3 = x^0x3 + t3.Mul(&x, z) + + // Step 3: t1 = x^0x5 + t1.Mul(z, t3) + + // Step 4: t6 = x^0x7 + t6.Mul(z, t1) + + // Step 5: t2 = x^0x9 + t2.Mul(z, t6) + + // Step 6: t0 = x^0xb + t0.Mul(z, t2) + + // Step 7: t4 = x^0xd + t4.Mul(z, t0) + + // Step 8: t5 = x^0xf + t5.Mul(z, t4) + + // Step 9: t7 = x^0x18 + t7.Mul(t2, t5) + + // Step 10: z = x^0x1f + z.Mul(t6, t7) + + // Step 14: t7 = x^0x180 + for s := 0; s < 4; s++ { + t7.Square(t7) + } + + // Step 15: t7 = x^0x183 + t7.Mul(t3, t7) + + // Step 18: t7 = x^0xc18 + for s := 0; s < 3; s++ { + t7.Square(t7) + } + + // Step 19: t7 = x^0xc19 + t7.Mul(&x, t7) + + // Step 26: t7 = x^0x60c80 + for s := 0; s < 7; s++ { + t7.Square(t7) + } + + // Step 27: t7 = x^0x60c89 + t7.Mul(t2, t7) + + // Step 29: t7 = x^0x183224 + for s := 0; s < 2; s++ { + t7.Square(t7) + } + + // Step 30: t7 = x^0x183227 + t7.Mul(t3, t7) + + // Step 35: t7 = x^0x30644e0 + for s := 0; s < 5; s++ { + t7.Square(t7) + } + + // Step 36: t6 = x^0x30644e7 + t6.Mul(t6, t7) + + // Step 42: t6 = x^0xc19139c0 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 43: t6 = x^0xc19139cb + t6.Mul(t0, t6) + + // Step 44: t6 = x^0x183227396 + t6.Square(t6) + + // Step 45: t6 = x^0x183227397 + t6.Mul(&x, t6) + + // Step 53: t6 = x^0x18322739700 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 54: t6 = x^0x18322739709 + t6.Mul(t2, t6) + + // Step 55: t6 = x^0x30644e72e12 + t6.Square(t6) + + // Step 56: t6 = x^0x30644e72e13 + t6.Mul(&x, t6) + + // Step 63: t6 = x^0x1832273970980 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 64: t6 = x^0x183227397098d + t6.Mul(t4, t6) + + // Step 74: t6 = x^0x60c89ce5c263400 + for s := 0; s < 10; s++ { + t6.Square(t6) + } + + // Step 75: t6 = x^0x60c89ce5c263405 + t6.Mul(t1, t6) + + // Step 81: t6 = x^0x183227397098d0140 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 82: t6 = x^0x183227397098d014d + t6.Mul(t4, t6) + + // Step 84: t6 = x^0x60c89ce5c26340534 + for s := 0; s < 2; s++ { + t6.Square(t6) + } + + // Step 85: t6 = x^0x60c89ce5c26340537 + t6.Mul(t3, t6) + + // Step 92: t6 = x^0x30644e72e131a029b80 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 93: t6 = x^0x30644e72e131a029b85 + t6.Mul(t1, t6) + + // Step 99: t6 = x^0xc19139cb84c680a6e140 + for s := 0; s < 6; s++ { + t6.Square(t6) + } + + // Step 100: t6 = x^0xc19139cb84c680a6e141 + t6.Mul(&x, t6) + + // Step 107: t6 = x^0x60c89ce5c263405370a080 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 108: t6 = x^0x60c89ce5c263405370a08b + t6.Mul(t0, t6) + + // Step 113: t6 = x^0xc19139cb84c680a6e141160 + for s := 0; s < 5; s++ { + t6.Square(t6) + } + + // Step 114: t6 = x^0xc19139cb84c680a6e14116d + t6.Mul(t4, t6) + + // Step 117: t6 = x^0x60c89ce5c263405370a08b68 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 118: t6 = x^0x60c89ce5c263405370a08b6d + t6.Mul(t1, t6) + + // Step 126: t6 = x^0x60c89ce5c263405370a08b6d00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 127: t6 = x^0x60c89ce5c263405370a08b6d03 + t6.Mul(t3, t6) + + // Step 136: t6 = x^0xc19139cb84c680a6e14116da0600 + for s := 0; s < 9; s++ { + t6.Square(t6) + } + + // Step 137: t6 = x^0xc19139cb84c680a6e14116da0605 + t6.Mul(t1, t6) + + // Step 140: t6 = x^0x60c89ce5c263405370a08b6d03028 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 141: t6 = x^0x60c89ce5c263405370a08b6d0302b + t6.Mul(t3, t6) + + // Step 149: t6 = x^0x60c89ce5c263405370a08b6d0302b00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 150: t6 = x^0x60c89ce5c263405370a08b6d0302b0b + t6.Mul(t0, t6) + + // Step 153: t6 = x^0x30644e72e131a029b85045b681815858 + for s := 0; s < 3; s++ { + t6.Square(t6) + } + + // Step 154: t6 = x^0x30644e72e131a029b85045b68181585d + t6.Mul(t1, t6) + + // Step 159: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba0 + for s := 0; s < 5; s++ { + t6.Square(t6) + } + + // Step 160: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5 + t6.Mul(t1, t6) + + // Step 167: t6 = x^0x30644e72e131a029b85045b68181585d280 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 168: t6 = x^0x30644e72e131a029b85045b68181585d283 + t6.Mul(t3, t6) + + // Step 175: t6 = x^0x183227397098d014dc2822db40c0ac2e94180 + for s := 0; s < 7; s++ { + t6.Square(t6) + } + + // Step 176: t6 = x^0x183227397098d014dc2822db40c0ac2e9419f + t6.Mul(z, t6) + + // Step 178: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067c + for s := 0; s < 2; s++ { + t6.Square(t6) + } + + // Step 179: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d + t6.Mul(&x, t6) + + // Step 187: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d00 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 188: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d09 + t6.Mul(t2, t6) + + // Step 196: t6 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d0900 + for s := 0; s < 8; s++ { + t6.Square(t6) + } + + // Step 197: t5 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f + t5.Mul(t5, t6) + + // Step 203: t5 = x^0x183227397098d014dc2822db40c0ac2e9419f4243c0 + for s := 0; s < 6; s++ { + t5.Square(t5) + } + + // Step 204: t4 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cd + t4.Mul(t4, t5) + + // Step 206: t4 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f34 + for s := 0; s < 2; s++ { + t4.Square(t4) + } + + // Step 207: t3 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f37 + t3.Mul(t3, t4) + + // Step 213: t3 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdc0 + for s := 0; s < 6; s++ { + t3.Square(t3) + } + + // Step 214: t3 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb + t3.Mul(t0, t3) + + // Step 215: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b96 + t3.Square(t3) + + // Step 216: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b97 + t3.Mul(&x, t3) + + // Step 224: t3 = x^0x30644e72e131a029b85045b68181585d2833e84879b9700 + for s := 0; s < 8; s++ { + t3.Square(t3) + } + + // Step 225: t2 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709 + t2.Mul(t2, t3) + + // Step 231: t2 = x^0xc19139cb84c680a6e14116da06056174a0cfa121e6e5c240 + for s := 0; s < 6; s++ { + t2.Square(t2) + } + + // Step 232: t1 = x^0xc19139cb84c680a6e14116da06056174a0cfa121e6e5c245 + t1.Mul(t1, t2) + + // Step 241: t1 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a00 + for s := 0; s < 9; s++ { + t1.Square(t1) + } + + // Step 242: t1 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f + t1.Mul(z, t1) + + // Step 251: t1 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e00 + for s := 0; s < 9; s++ { + t1.Square(t1) + } + + // Step 252: t1 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f + t1.Mul(z, t1) + + // Step 257: t1 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3e0 + for s := 0; s < 5; s++ { + t1.Square(t1) + } + + // Step 258: t0 = x^0x60c89ce5c263405370a08b6d0302b0ba5067d090f372e12287c3eb + t0.Mul(t0, t1) + + // Step 261: t0 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f58 + for s := 0; s < 3; s++ { + t0.Square(t0) + } + + // Step 262: t0 = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f59 + t0.Mul(&x, t0) + + // Step 269: t0 = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac80 + for s := 0; s < 7; s++ { + t0.Square(t0) + } + + // Step 270: z = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f + z.Mul(z, t0) + + // Step 271: z = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593e + z.Square(z) + + // Step 272: z = x^0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f + z.Mul(&x, z) + + // Step 299: z = x^0x183227397098d014dc2822db40c0ac2e9419f4243cdcb848a1f0fac9f8000000 + for s := 0; s < 27; s++ { + z.Square(z) + } + + return z +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_fuzz.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_fuzz.go new file mode 100644 index 00000000000..a4c87eb250f --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_fuzz.go @@ -0,0 +1,136 @@ +//go:build gofuzz +// +build gofuzz + +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package fr + +import ( + "bytes" + "encoding/binary" + "io" + "math/big" + "math/bits" +) + +const ( + fuzzInteresting = 1 + fuzzNormal = 0 + fuzzDiscard = -1 +) + +// Fuzz arithmetic operations fuzzer +func Fuzz(data []byte) int { + r := bytes.NewReader(data) + + var e1, e2 Element + e1.SetRawBytes(r) + e2.SetRawBytes(r) + + { + // mul assembly + + var c, _c Element + a, _a, b, _b := e1, e1, e2, e2 + c.Mul(&a, &b) + _mulGeneric(&_c, &_a, &_b) + + if !c.Equal(&_c) { + panic("mul asm != mul generic on Element") + } + } + + { + // inverse + inv := e1 + inv.Inverse(&inv) + + var bInv, b1, b2 big.Int + e1.ToBigIntRegular(&b1) + bInv.ModInverse(&b1, Modulus()) + inv.ToBigIntRegular(&b2) + + if b2.Cmp(&bInv) != 0 { + panic("inverse operation doesn't match big int result") + } + } + + { + // a + -a == 0 + a, b := e1, e1 + b.Neg(&b) + a.Add(&a, &b) + if !a.IsZero() { + panic("a + -a != 0") + } + } + + return fuzzNormal + +} + +// SetRawBytes reads up to Bytes (bytes needed to represent Element) from reader +// and interpret it as big endian uint64 +// used for fuzzing purposes only +func (z *Element) SetRawBytes(r io.Reader) { + + buf := make([]byte, 8) + + for i := 0; i < len(z); i++ { + if _, err := io.ReadFull(r, buf); err != nil { + goto eof + } + z[i] = binary.BigEndian.Uint64(buf[:]) + } +eof: + z[3] %= qElement[3] + + if z.BiggerModulus() { + var b uint64 + z[0], b = bits.Sub64(z[0], qElement[0], 0) + z[1], b = bits.Sub64(z[1], qElement[1], b) + z[2], b = bits.Sub64(z[2], qElement[2], b) + z[3], b = bits.Sub64(z[3], qElement[3], b) + } + + return +} + +func (z *Element) BiggerModulus() bool { + if z[3] > qElement[3] { + return true + } + if z[3] < qElement[3] { + return false + } + + if z[2] > qElement[2] { + return true + } + if z[2] < qElement[2] { + return false + } + + if z[1] > qElement[1] { + return true + } + if z[1] < qElement[1] { + return false + } + + return z[0] >= qElement[0] +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_adx_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_adx_amd64.s index 494e7bfd7e2..bc0b747d255 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_adx_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_adx_amd64.s @@ -46,7 +46,7 @@ GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 TEXT ·mul(SB), NOSPLIT, $0-24 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // however, to benefit from the ADCX and ADOX carry chains // we split the inner loops in 2: // for i=0 to N-1 @@ -72,7 +72,7 @@ TEXT ·mul(SB), NOSPLIT, $0-24 // A -> BP // t[0] -> R14 - // t[1] -> R15 + // t[1] -> R13 // t[2] -> CX // t[3] -> BX // clear the flags @@ -80,11 +80,11 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ 0(R11), DX // (A,t[0]) := x[0]*y[0] + A - MULXQ DI, R14, R15 + MULXQ DI, R14, R13 // (A,t[1]) := x[1]*y[0] + A MULXQ R8, AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := x[2]*y[0] + A MULXQ R9, AX, BX @@ -111,14 +111,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -139,9 +139,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[1] + A ADCXQ BP, CX @@ -171,14 +171,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -199,9 +199,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[2] + A ADCXQ BP, CX @@ -231,14 +231,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -259,9 +259,9 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[3] + A ADCXQ BP, CX @@ -291,14 +291,14 @@ TEXT ·mul(SB), NOSPLIT, $0-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -310,12 +310,12 @@ TEXT ·mul(SB), NOSPLIT, $0-24 ADCXQ AX, BX ADOXQ BP, BX - // reduce element(R14,R15,CX,BX) using temp registers (R13,SI,R12,R11) - REDUCE(R14,R15,CX,BX,R13,SI,R12,R11) + // reduce element(R14,R13,CX,BX) using temp registers (SI,R12,R11,DI) + REDUCE(R14,R13,CX,BX,SI,R12,R11,DI) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET @@ -323,7 +323,7 @@ TEXT ·mul(SB), NOSPLIT, $0-24 TEXT ·fromMont(SB), NOSPLIT, $0-8 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // when y = 1 we have: // for i=0 to N-1 // t[i] = x[i] @@ -335,7 +335,7 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 // t[N-1] = C MOVQ res+0(FP), DX MOVQ 0(DX), R14 - MOVQ 8(DX), R15 + MOVQ 8(DX), R13 MOVQ 16(DX), CX MOVQ 24(DX), BX XORQ DX, DX @@ -351,14 +351,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -380,14 +380,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -409,14 +409,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -438,14 +438,14 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -455,12 +455,12 @@ TEXT ·fromMont(SB), NOSPLIT, $0-8 ADCXQ AX, BX ADOXQ AX, BX - // reduce element(R14,R15,CX,BX) using temp registers (SI,DI,R8,R9) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9) + // reduce element(R14,R13,CX,BX) using temp registers (SI,DI,R8,R9) + REDUCE(R14,R13,CX,BX,SI,DI,R8,R9) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_amd64.s index 38b3b6cf629..764b9c43285 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_mul_amd64.s @@ -46,7 +46,7 @@ GLOBL qInv0<>(SB), (RODATA+NOPTR), $8 TEXT ·mul(SB), $24-24 // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // however, to benefit from the ADCX and ADOX carry chains // we split the inner loops in 2: // for i=0 to N-1 @@ -75,7 +75,7 @@ TEXT ·mul(SB), $24-24 // A -> BP // t[0] -> R14 - // t[1] -> R15 + // t[1] -> R13 // t[2] -> CX // t[3] -> BX // clear the flags @@ -83,11 +83,11 @@ TEXT ·mul(SB), $24-24 MOVQ 0(R11), DX // (A,t[0]) := x[0]*y[0] + A - MULXQ DI, R14, R15 + MULXQ DI, R14, R13 // (A,t[1]) := x[1]*y[0] + A MULXQ R8, AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := x[2]*y[0] + A MULXQ R9, AX, BX @@ -114,14 +114,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -142,9 +142,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[1] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[1] + A ADCXQ BP, CX @@ -174,14 +174,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -202,9 +202,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[2] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[2] + A ADCXQ BP, CX @@ -234,14 +234,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -262,9 +262,9 @@ TEXT ·mul(SB), $24-24 ADOXQ AX, R14 // (A,t[1]) := t[1] + x[1]*y[3] + A - ADCXQ BP, R15 + ADCXQ BP, R13 MULXQ R8, AX, BP - ADOXQ AX, R15 + ADOXQ AX, R13 // (A,t[2]) := t[2] + x[2]*y[3] + A ADCXQ BP, CX @@ -294,14 +294,14 @@ TEXT ·mul(SB), $24-24 MOVQ R12, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -313,12 +313,12 @@ TEXT ·mul(SB), $24-24 ADCXQ AX, BX ADOXQ BP, BX - // reduce element(R14,R15,CX,BX) using temp registers (R13,SI,R12,R11) - REDUCE(R14,R15,CX,BX,R13,SI,R12,R11) + // reduce element(R14,R13,CX,BX) using temp registers (SI,R12,R11,DI) + REDUCE(R14,R13,CX,BX,SI,R12,R11,DI) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET @@ -337,7 +337,7 @@ TEXT ·fromMont(SB), $8-8 NO_LOCAL_POINTERS // the algorithm is described here - // https://hackmd.io/@zkteam/modular_multiplication + // https://hackmd.io/@gnark/modular_multiplication // when y = 1 we have: // for i=0 to N-1 // t[i] = x[i] @@ -351,7 +351,7 @@ TEXT ·fromMont(SB), $8-8 JNE l2 MOVQ res+0(FP), DX MOVQ 0(DX), R14 - MOVQ 8(DX), R15 + MOVQ 8(DX), R13 MOVQ 16(DX), CX MOVQ 24(DX), BX XORQ DX, DX @@ -367,14 +367,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -396,14 +396,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -425,14 +425,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -454,14 +454,14 @@ TEXT ·fromMont(SB), $8-8 MOVQ BP, R14 // (C,t[0]) := t[1] + m*q[1] + C - ADCXQ R15, R14 - MULXQ q<>+8(SB), AX, R15 + ADCXQ R13, R14 + MULXQ q<>+8(SB), AX, R13 ADOXQ AX, R14 // (C,t[1]) := t[2] + m*q[2] + C - ADCXQ CX, R15 + ADCXQ CX, R13 MULXQ q<>+16(SB), AX, CX - ADOXQ AX, R15 + ADOXQ AX, R13 // (C,t[2]) := t[3] + m*q[3] + C ADCXQ BX, CX @@ -471,12 +471,12 @@ TEXT ·fromMont(SB), $8-8 ADCXQ AX, BX ADOXQ AX, BX - // reduce element(R14,R15,CX,BX) using temp registers (SI,DI,R8,R9) - REDUCE(R14,R15,CX,BX,SI,DI,R8,R9) + // reduce element(R14,R13,CX,BX) using temp registers (SI,DI,R8,R9) + REDUCE(R14,R13,CX,BX,SI,DI,R8,R9) MOVQ res+0(FP), AX MOVQ R14, 0(AX) - MOVQ R15, 8(AX) + MOVQ R13, 8(AX) MOVQ CX, 16(AX) MOVQ BX, 24(AX) RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.go index f0d8316e528..78022b3e6f7 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.go @@ -22,6 +22,9 @@ func MulBy3(x *Element) //go:noescape func MulBy5(x *Element) +//go:noescape +func MulBy13(x *Element) + //go:noescape func add(res, x, y *Element) @@ -42,3 +45,6 @@ func fromMont(res *Element) //go:noescape func reduce(res *Element) + +//go:noescape +func Butterfly(a, b *Element) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.s index 83133a8f5eb..d5dca83d259 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_amd64.s @@ -233,3 +233,108 @@ TEXT ·MulBy5(SB), NOSPLIT, $0-8 MOVQ BX, 16(AX) MOVQ SI, 24(AX) RET + +// MulBy13(x *Element) +TEXT ·MulBy13(SB), NOSPLIT, $0-8 + MOVQ x+0(FP), AX + MOVQ 0(AX), DX + MOVQ 8(AX), CX + MOVQ 16(AX), BX + MOVQ 24(AX), SI + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (R11,R12,R13,R14) + REDUCE(DX,CX,BX,SI,R11,R12,R13,R14) + + MOVQ DX, R11 + MOVQ CX, R12 + MOVQ BX, R13 + MOVQ SI, R14 + ADDQ DX, DX + ADCQ CX, CX + ADCQ BX, BX + ADCQ SI, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ R11, DX + ADCQ R12, CX + ADCQ R13, BX + ADCQ R14, SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + ADDQ 0(AX), DX + ADCQ 8(AX), CX + ADCQ 16(AX), BX + ADCQ 24(AX), SI + + // reduce element(DX,CX,BX,SI) using temp registers (DI,R8,R9,R10) + REDUCE(DX,CX,BX,SI,DI,R8,R9,R10) + + MOVQ DX, 0(AX) + MOVQ CX, 8(AX) + MOVQ BX, 16(AX) + MOVQ SI, 24(AX) + RET + +// Butterfly(a, b *Element) sets a = a + b; b = a - b +TEXT ·Butterfly(SB), NOSPLIT, $0-16 + MOVQ a+0(FP), AX + MOVQ 0(AX), CX + MOVQ 8(AX), BX + MOVQ 16(AX), SI + MOVQ 24(AX), DI + MOVQ CX, R8 + MOVQ BX, R9 + MOVQ SI, R10 + MOVQ DI, R11 + XORQ AX, AX + MOVQ b+8(FP), DX + ADDQ 0(DX), CX + ADCQ 8(DX), BX + ADCQ 16(DX), SI + ADCQ 24(DX), DI + SUBQ 0(DX), R8 + SBBQ 8(DX), R9 + SBBQ 16(DX), R10 + SBBQ 24(DX), R11 + MOVQ $0x43e1f593f0000001, R12 + MOVQ $0x2833e84879b97091, R13 + MOVQ $0xb85045b68181585d, R14 + MOVQ $0x30644e72e131a029, R15 + CMOVQCC AX, R12 + CMOVQCC AX, R13 + CMOVQCC AX, R14 + CMOVQCC AX, R15 + ADDQ R12, R8 + ADCQ R13, R9 + ADCQ R14, R10 + ADCQ R15, R11 + MOVQ R8, 0(DX) + MOVQ R9, 8(DX) + MOVQ R10, 16(DX) + MOVQ R11, 24(DX) + + // reduce element(CX,BX,SI,DI) using temp registers (R8,R9,R10,R11) + REDUCE(CX,BX,SI,DI,R8,R9,R10,R11) + + MOVQ a+0(FP), AX + MOVQ CX, 0(AX) + MOVQ BX, 8(AX) + MOVQ SI, 16(AX) + MOVQ DI, 24(AX) + RET diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_noasm.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_noasm.go index e7daa4d40ee..ec1fac18d63 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_noasm.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/element_ops_noasm.go @@ -1,3 +1,4 @@ +//go:build !amd64 // +build !amd64 // Copyright 2020 ConsenSys Software Inc. @@ -34,6 +35,18 @@ func MulBy5(x *Element) { mulByConstant(x, 5) } +// MulBy13 x *= 13 +func MulBy13(x *Element) { + mulByConstant(x, 13) +} + +// Butterfly sets +// a = a + b +// b = a - b +func Butterfly(a, b *Element) { + _butterflyGeneric(a, b) +} + func mul(z, x, y *Element) { _mulGeneric(z, x, y) } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/doc.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/doc.go new file mode 100644 index 00000000000..497bd40a972 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/doc.go @@ -0,0 +1,18 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +// Package mimc provides MiMC hash function using Miyaguchi–Preneel construction. +package mimc diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/fuzz.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/fuzz.go new file mode 100644 index 00000000000..41b557cf3f7 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/fuzz.go @@ -0,0 +1,34 @@ +//go:build gofuzz +// +build gofuzz + +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package mimc + +const ( + fuzzInteresting = 1 + fuzzNormal = 0 + fuzzDiscard = -1 +) + +func Fuzz(data []byte) int { + var s []byte + h := NewMiMC(string(data)) + h.Write(data) + h.Sum(s) + return fuzzNormal +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/mimc.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/mimc.go new file mode 100644 index 00000000000..22492ad185f --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc/mimc.go @@ -0,0 +1,174 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package mimc + +import ( + "hash" + "math/big" + + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "golang.org/x/crypto/sha3" +) + +const mimcNbRounds = 91 + +// BlockSize size that mimc consumes +const BlockSize = fr.Bytes + +// Params constants for the mimc hash function +type Params []fr.Element + +// NewParams creates new mimc object +func NewParams(seed string) Params { + + // set the constants + res := make(Params, mimcNbRounds) + + rnd := sha3.Sum256([]byte(seed)) + value := new(big.Int).SetBytes(rnd[:]) + + for i := 0; i < mimcNbRounds; i++ { + rnd = sha3.Sum256(value.Bytes()) + value.SetBytes(rnd[:]) + res[i].SetBigInt(value) + } + + return res +} + +// digest represents the partial evaluation of the checksum +// along with the params of the mimc function +type digest struct { + Params Params + h fr.Element + data []byte // data to hash +} + +// NewMiMC returns a MiMCImpl object, pure-go reference implementation +func NewMiMC(seed string) hash.Hash { + d := new(digest) + params := NewParams(seed) + //d.Reset() + d.Params = params + d.Reset() + return d +} + +// Reset resets the Hash to its initial state. +func (d *digest) Reset() { + d.data = nil + d.h = fr.Element{0, 0, 0, 0} +} + +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (d *digest) Sum(b []byte) []byte { + buffer := d.checksum() + d.data = nil // flush the data already hashed + hash := buffer.Bytes() + b = append(b, hash[:]...) + return b +} + +// BlockSize returns the hash's underlying block size. +// The Write method must be able to accept any amount +// of data, but it may operate more efficiently if all writes +// are a multiple of the block size. +func (d *digest) Size() int { + return BlockSize +} + +// BlockSize returns the number of bytes Sum will return. +func (d *digest) BlockSize() int { + return BlockSize +} + +// Write (via the embedded io.Writer interface) adds more data to the running hash. +// It never returns an error. +func (d *digest) Write(p []byte) (n int, err error) { + n = len(p) + d.data = append(d.data, p...) + return +} + +// Hash hash using Miyaguchi–Preneel: +// https://en.wikipedia.org/wiki/One-way_compression_function +// The XOR operation is replaced by field addition, data is in Montgomery form +func (d *digest) checksum() fr.Element { + + var buffer [BlockSize]byte + var x fr.Element + + // if data size is not multiple of BlockSizes we padd: + // .. || 0xaf8 -> .. || 0x0000...0af8 + if len(d.data)%BlockSize != 0 { + q := len(d.data) / BlockSize + r := len(d.data) % BlockSize + sliceq := make([]byte, q*BlockSize) + copy(sliceq, d.data) + slicer := make([]byte, r) + copy(slicer, d.data[q*BlockSize:]) + sliceremainder := make([]byte, BlockSize-r) + d.data = append(sliceq, sliceremainder...) + d.data = append(d.data, slicer...) + } + + if len(d.data) == 0 { + d.data = make([]byte, 32) + } + + nbChunks := len(d.data) / BlockSize + + for i := 0; i < nbChunks; i++ { + copy(buffer[:], d.data[i*BlockSize:(i+1)*BlockSize]) + x.SetBytes(buffer[:]) + d.encrypt(x) + d.h.Add(&x, &d.h) + } + + return d.h +} + +// plain execution of a mimc run +// m: message +// k: encryption key +func (d *digest) encrypt(m fr.Element) { + + for i := 0; i < len(d.Params); i++ { + // m = (m+k+c)^5 + var tmp fr.Element + tmp.Add(&m, &d.h).Add(&tmp, &d.Params[i]) + m.Square(&tmp). + Square(&m). + Mul(&m, &tmp) + } + m.Add(&m, &d.h) + d.h = m +} + +// Sum computes the mimc hash of msg from seed +func Sum(seed string, msg []byte) ([]byte, error) { + params := NewParams(seed) + var d digest + d.Params = params + if _, err := d.Write(msg); err != nil { + return nil, err + } + h := d.checksum() + bytes := h.Bytes() + return bytes[:], nil +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fuzz.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fuzz.go new file mode 100644 index 00000000000..69b9c450507 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/fuzz.go @@ -0,0 +1,76 @@ +//go:build gofuzz +// +build gofuzz + +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package bn254 + +import ( + "bytes" + "github.com/consensys/gnark-crypto/ecc/bn254/fp" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" + "math/big" +) + +const ( + fuzzInteresting = 1 + fuzzNormal = 0 + fuzzDiscard = -1 +) + +func Fuzz(data []byte) int { + // TODO separate in multiple FuzzXXX and update continuous fuzzer scripts + // else, we don't really benefits for fuzzer strategy. + fr.Fuzz(data) + fp.Fuzz(data) + mimc.Fuzz(data) + + // fuzz pairing + r := bytes.NewReader(data) + var e1, e2 fr.Element + e1.SetRawBytes(r) + e2.SetRawBytes(r) + + { + var r, r1, r2, r1r2, zero GT + var b1, b2, b1b2 big.Int + e1.ToBigIntRegular(&b1) + e2.ToBigIntRegular(&b2) + b1b2.Mul(&b1, &b2) + + var p1 G1Affine + var p2 G2Affine + + p1.ScalarMultiplication(&g1GenAff, &b1) + p2.ScalarMultiplication(&g2GenAff, &b2) + + r, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{g2GenAff}) + r1, _ = Pair([]G1Affine{p1}, []G2Affine{g2GenAff}) + r2, _ = Pair([]G1Affine{g1GenAff}, []G2Affine{p2}) + + r1r2.Exp(&r, b1b2) + r1.Exp(&r1, b2) + r2.Exp(&r2, b1) + + if !(r1r2.Equal(&r1) && r1r2.Equal(&r2) && !r.Equal(&zero)) { + panic("pairing bilinearity check failed") + } + } + + return fuzzNormal +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g1.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g1.go index 787bdc0411e..8a5f823d00e 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g1.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g1.go @@ -17,7 +17,9 @@ package bn254 import ( + "math" "math/big" + "runtime" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fp" @@ -58,6 +60,30 @@ func (p *G1Affine) ScalarMultiplication(a *G1Affine, s *big.Int) *G1Affine { return p } +// Add adds two point in affine coordinates. +// This should rarely be used as it is very inneficient compared to Jacobian +// TODO implement affine addition formula +func (p *G1Affine) Add(a, b *G1Affine) *G1Affine { + var p1, p2 G1Jac + p1.FromAffine(a) + p2.FromAffine(b) + p1.AddAssign(&p2) + p.FromJacobian(&p1) + return p +} + +// Sub subs two point in affine coordinates. +// This should rarely be used as it is very inneficient compared to Jacobian +// TODO implement affine addition formula +func (p *G1Affine) Sub(a, b *G1Affine) *G1Affine { + var p1, p2 G1Jac + p1.FromAffine(a) + p2.FromAffine(b) + p1.SubAssign(&p2) + p.FromJacobian(&p1) + return p +} + // Equal tests if two points (in Affine coordinates) are equal func (p *G1Affine) Equal(a *G1Affine) bool { return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) @@ -112,7 +138,7 @@ func (p *G1Affine) IsOnCurve() bool { func (p *G1Affine) IsInSubGroup() bool { var _p G1Jac _p.FromAffine(p) - return _p.IsOnCurve() && _p.IsInSubGroup() + return _p.IsInSubGroup() } // ------------------------------------------------------------------------------------------------- @@ -391,7 +417,6 @@ func (p *G1Jac) phi(a *G1Jac) *G1Jac { func (p *G1Jac) mulGLV(a *G1Jac, s *big.Int) *G1Jac { var table [15]G1Jac - var zero big.Int var res G1Jac var k1, k2 fr.Element @@ -404,11 +429,11 @@ func (p *G1Jac) mulGLV(a *G1Jac, s *big.Int) *G1Jac { // split the scalar, modifies +-a, phi(a) accordingly k := ecc.SplitScalar(s, &glvBasis) - if k[0].Cmp(&zero) == -1 { + if k[0].Sign() == -1 { k[0].Neg(&k[0]) table[0].Neg(&table[0]) } - if k[1].Cmp(&zero) == -1 { + if k[1].Sign() == -1 { k[1].Neg(&k[1]) table[3].Neg(&table[3]) } @@ -434,7 +459,7 @@ func (p *G1Jac) mulGLV(a *G1Jac, s *big.Int) *G1Jac { k2.SetBigInt(&k[1]).FromMont() // loop starts from len(k1)/2 due to the bounds - for i := len(k1)/2 - 1; i >= 0; i-- { + for i := int(math.Ceil(fr.Limbs/2. - 1)); i >= 0; i-- { mask := uint64(3) << 62 for j := 0; j < 32; j++ { res.Double(&res).Double(&res) @@ -753,10 +778,10 @@ func (p *g1JacExtended) doubleMixed(q *G1Affine) *g1JacExtended { return p } -// BatchJacobianToAffineG1Affine converts points in Jacobian coordinates to Affine coordinates +// BatchJacobianToAffineG1 converts points in Jacobian coordinates to Affine coordinates // performing a single field inversion (Montgomery batch inversion trick) // result must be allocated with len(result) == len(points) -func BatchJacobianToAffineG1Affine(points []G1Jac, result []G1Affine) { +func BatchJacobianToAffineG1(points []G1Jac, result []G1Affine) { zeroes := make([]bool, len(points)) accumulator := fp.One() @@ -843,7 +868,7 @@ func BatchScalarMultiplicationG1(base *G1Affine, scalars []fr.Element) []G1Affin baseTable[i].AddMixed(base) } - pScalars := partitionScalars(scalars, c) + pScalars, _ := partitionScalars(scalars, c, false, runtime.NumCPU()) // compute offset and word selector / shift to select the right bits of our windows selectors := make([]selector, nbChunks) @@ -863,7 +888,7 @@ func BatchScalarMultiplicationG1(base *G1Affine, scalars []fr.Element) []G1Affin } // convert our base exp table into affine to use AddMixed baseTableAff := make([]G1Affine, (1 << (c - 1))) - BatchJacobianToAffineG1Affine(baseTable, baseTableAff) + BatchJacobianToAffineG1(baseTable, baseTableAff) toReturn := make([]G1Jac, len(scalars)) // for each digit, take value in the base table, double it c time, voila. @@ -906,6 +931,6 @@ func BatchScalarMultiplicationG1(base *G1Affine, scalars []fr.Element) []G1Affin } }) toReturnAff := make([]G1Affine, len(scalars)) - BatchJacobianToAffineG1Affine(toReturn, toReturnAff) + BatchJacobianToAffineG1(toReturn, toReturnAff) return toReturnAff } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g2.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g2.go index a037b715136..6c918b5da62 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g2.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/g2.go @@ -17,7 +17,9 @@ package bn254 import ( + "math" "math/big" + "runtime" "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" @@ -63,6 +65,30 @@ func (p *G2Affine) ScalarMultiplication(a *G2Affine, s *big.Int) *G2Affine { return p } +// Add adds two point in affine coordinates. +// This should rarely be used as it is very inneficient compared to Jacobian +// TODO implement affine addition formula +func (p *G2Affine) Add(a, b *G2Affine) *G2Affine { + var p1, p2 G2Jac + p1.FromAffine(a) + p2.FromAffine(b) + p1.AddAssign(&p2) + p.FromJacobian(&p1) + return p +} + +// Sub subs two point in affine coordinates. +// This should rarely be used as it is very inneficient compared to Jacobian +// TODO implement affine addition formula +func (p *G2Affine) Sub(a, b *G2Affine) *G2Affine { + var p1, p2 G2Jac + p1.FromAffine(a) + p2.FromAffine(b) + p1.SubAssign(&p2) + p.FromJacobian(&p1) + return p +} + // Equal tests if two points (in Affine coordinates) are equal func (p *G2Affine) Equal(a *G2Affine) bool { return p.X.Equal(&a.X) && p.Y.Equal(&a.Y) @@ -117,7 +143,7 @@ func (p *G2Affine) IsOnCurve() bool { func (p *G2Affine) IsInSubGroup() bool { var _p G2Jac _p.FromAffine(p) - return _p.IsOnCurve() && _p.IsInSubGroup() + return _p.IsInSubGroup() } // ------------------------------------------------------------------------------------------------- @@ -346,20 +372,12 @@ func (p *G2Jac) IsOnCurve() bool { } // IsInSubGroup returns true if p is on the r-torsion, false otherwise. -// Z[r,0]+Z[-lambdaG2Affine, 1] is the kernel -// of (u,v)->u+lambdaG2Affinev mod r. Expressing r, lambdaG2Affine as -// polynomials in x, a short vector of this Zmodule is -// (4x+2), (-12x**2+4*x). So we check that (4x+2)p+(-12x**2+4*x)phi(p) -// is the infinity. +// [r]P == 0 <==> Frob(P) == [6x^2]P func (p *G2Jac) IsInSubGroup() bool { - - var res, xphip, phip G2Jac - phip.phi(p) - xphip.ScalarMultiplication(&phip, &xGen) // x*phi(p) - res.Double(&xphip).AddAssign(&xphip) // 3x*phi(p) - res.AddAssign(&phip).SubAssign(p) // 3x*phi(p)+phi(p)-p - res.Double(&res).ScalarMultiplication(&res, &xGen) // 6x**2*phi(p)+2x*phi(p)-2x*p - res.SubAssign(p).Double(&res) // 12x**2*phi(p)+4x*phi(p)-4x*p-2p + var a, res G2Jac + a.psi(p) + res.ScalarMultiplication(p, &fixedCoeff). + SubAssign(&a) return res.IsOnCurve() && res.Z.IsZero() @@ -416,7 +434,6 @@ func (p *G2Jac) phi(a *G2Jac) *G2Jac { func (p *G2Jac) mulGLV(a *G2Jac, s *big.Int) *G2Jac { var table [15]G2Jac - var zero big.Int var res G2Jac var k1, k2 fr.Element @@ -429,11 +446,11 @@ func (p *G2Jac) mulGLV(a *G2Jac, s *big.Int) *G2Jac { // split the scalar, modifies +-a, phi(a) accordingly k := ecc.SplitScalar(s, &glvBasis) - if k[0].Cmp(&zero) == -1 { + if k[0].Sign() == -1 { k[0].Neg(&k[0]) table[0].Neg(&table[0]) } - if k[1].Cmp(&zero) == -1 { + if k[1].Sign() == -1 { k[1].Neg(&k[1]) table[3].Neg(&table[3]) } @@ -459,7 +476,7 @@ func (p *G2Jac) mulGLV(a *G2Jac, s *big.Int) *G2Jac { k2.SetBigInt(&k[1]).FromMont() // loop starts from len(k1)/2 due to the bounds - for i := len(k1)/2 - 1; i >= 0; i-- { + for i := int(math.Ceil(fr.Limbs/2. - 1)); i >= 0; i-- { mask := uint64(3) << 62 for j := 0; j < 32; j++ { res.Double(&res).Double(&res) @@ -477,7 +494,7 @@ func (p *G2Jac) mulGLV(a *G2Jac, s *big.Int) *G2Jac { return p } -// ClearCofactor ... +// ClearCofactor maps a point in curve to r-torsion func (p *G2Affine) ClearCofactor(a *G2Affine) *G2Affine { var _p G2Jac _p.FromAffine(a) @@ -486,7 +503,7 @@ func (p *G2Affine) ClearCofactor(a *G2Affine) *G2Affine { return p } -// ClearCofactor ... +// ClearCofactor maps a point in curve to r-torsion func (p *G2Jac) ClearCofactor(a *G2Jac) *G2Jac { // cf http://cacr.uwaterloo.ca/techreports/2011/cacr2011-26.pdf, 6.1 var points [4]G2Jac @@ -822,6 +839,13 @@ func (p *g2Proj) Set(a *g2Proj) *g2Proj { return p } +// Neg computes -G +func (p *g2Proj) Neg(a *g2Proj) *g2Proj { + *p = *a + p.y.Neg(&a.y) + return p +} + // FromJacobian converts a point from Jacobian to projective coordinates func (p *g2Proj) FromJacobian(Q *G2Jac) *g2Proj { var buf fptower.E2 @@ -890,7 +914,7 @@ func BatchScalarMultiplicationG2(base *G2Affine, scalars []fr.Element) []G2Affin baseTable[i].AddMixed(base) } - pScalars := partitionScalars(scalars, c) + pScalars, _ := partitionScalars(scalars, c, false, runtime.NumCPU()) // compute offset and word selector / shift to select the right bits of our windows selectors := make([]selector, nbChunks) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm.go index c7bb911dfb9..0ec192019d8 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm.go @@ -1,3 +1,4 @@ +//go:build !noadx // +build !noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm_noadx.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm_noadx.go index f09b13900c3..6a09c11c492 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm_noadx.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/asm_noadx.go @@ -1,3 +1,4 @@ +//go:build noadx // +build noadx // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12.go index ef2c170e43b..3f8c763fb80 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12.go @@ -20,7 +20,6 @@ import ( "encoding/binary" "errors" "github.com/consensys/gnark-crypto/ecc/bn254/fp" - "github.com/consensys/gnark-crypto/ecc/bn254/fr" "math/big" ) @@ -135,7 +134,186 @@ func (z *E12) Square(x *E12) *E12 { return z } -// CyclotomicSquare https://eprint.iacr.org/2009/565.pdf, 3.2 +// Karabina's compressed cyclotomic square +// https://eprint.iacr.org/2010/542.pdf +// Th. 3.2 with minor modifications to fit our tower +func (z *E12) CyclotomicSquareCompressed(x *E12) *E12 { + + var t [7]E2 + + // t0 = g1^2 + t[0].Square(&x.C0.B1) + // t1 = g5^2 + t[1].Square(&x.C1.B2) + // t5 = g1 + g5 + t[5].Add(&x.C0.B1, &x.C1.B2) + // t2 = (g1 + g5)^2 + t[2].Square(&t[5]) + + // t3 = g1^2 + g5^2 + t[3].Add(&t[0], &t[1]) + // t5 = 2 * g1 * g5 + t[5].Sub(&t[2], &t[3]) + + // t6 = g3 + g2 + t[6].Add(&x.C1.B0, &x.C0.B2) + // t3 = (g3 + g2)^2 + t[3].Square(&t[6]) + // t2 = g3^2 + t[2].Square(&x.C1.B0) + + // t6 = 2 * nr * g1 * g5 + t[6].MulByNonResidue(&t[5]) + // t5 = 4 * nr * g1 * g5 + 2 * g3 + t[5].Add(&t[6], &x.C1.B0). + Double(&t[5]) + // z3 = 6 * nr * g1 * g5 + 2 * g3 + z.C1.B0.Add(&t[5], &t[6]) + + // t4 = nr * g5^2 + t[4].MulByNonResidue(&t[1]) + // t5 = nr * g5^2 + g1^2 + t[5].Add(&t[0], &t[4]) + // t6 = nr * g5^2 + g1^2 - g2 + t[6].Sub(&t[5], &x.C0.B2) + + // t1 = g2^2 + t[1].Square(&x.C0.B2) + + // t6 = 2 * nr * g5^2 + 2 * g1^2 - 2*g2 + t[6].Double(&t[6]) + // z2 = 3 * nr * g5^2 + 3 * g1^2 - 2*g2 + z.C0.B2.Add(&t[6], &t[5]) + + // t4 = nr * g2^2 + t[4].MulByNonResidue(&t[1]) + // t5 = g3^2 + nr * g2^2 + t[5].Add(&t[2], &t[4]) + // t6 = g3^2 + nr * g2^2 - g1 + t[6].Sub(&t[5], &x.C0.B1) + // t6 = 2 * g3^2 + 2 * nr * g2^2 - 2 * g1 + t[6].Double(&t[6]) + // z1 = 3 * g3^2 + 3 * nr * g2^2 - 2 * g1 + z.C0.B1.Add(&t[6], &t[5]) + + // t0 = g2^2 + g3^2 + t[0].Add(&t[2], &t[1]) + // t5 = 2 * g3 * g2 + t[5].Sub(&t[3], &t[0]) + // t6 = 2 * g3 * g2 + g5 + t[6].Add(&t[5], &x.C1.B2) + // t6 = 4 * g3 * g2 + 2 * g5 + t[6].Double(&t[6]) + // z5 = 6 * g3 * g2 + 2 * g5 + z.C1.B2.Add(&t[5], &t[6]) + + return z +} + +// Decompress Karabina's cyclotomic square result +func (z *E12) Decompress(x *E12) *E12 { + + var t [3]E2 + var one E2 + one.SetOne() + + // t0 = g1^2 + t[0].Square(&x.C0.B1) + // t1 = 3 * g1^2 - 2 * g2 + t[1].Sub(&t[0], &x.C0.B2). + Double(&t[1]). + Add(&t[1], &t[0]) + // t0 = E * g5^2 + t1 + t[2].Square(&x.C1.B2) + t[0].MulByNonResidue(&t[2]). + Add(&t[0], &t[1]) + // t1 = 1/(4 * g3) + t[1].Double(&x.C1.B0). + Double(&t[1]). + Inverse(&t[1]) // costly + // z4 = g4 + z.C1.B1.Mul(&t[0], &t[1]) + + // t1 = g2 * g1 + t[1].Mul(&x.C0.B2, &x.C0.B1) + // t2 = 2 * g4^2 - 3 * g2 * g1 + t[2].Square(&z.C1.B1). + Sub(&t[2], &t[1]). + Double(&t[2]). + Sub(&t[2], &t[1]) + // t1 = g3 * g5 + t[1].Mul(&x.C1.B0, &x.C1.B2) + // c_0 = E * (2 * g4^2 + g3 * g5 - 3 * g2 * g1) + 1 + t[2].Add(&t[2], &t[1]) + z.C0.B0.MulByNonResidue(&t[2]). + Add(&z.C0.B0, &one) + + z.C0.B1.Set(&x.C0.B1) + z.C0.B2.Set(&x.C0.B2) + z.C1.B0.Set(&x.C1.B0) + z.C1.B2.Set(&x.C1.B2) + + return z +} + +// BatchDecompress multiple Karabina's cyclotomic square results +func BatchDecompress(x []E12) []E12 { + + n := len(x) + if n == 0 { + return x + } + + t0 := make([]E2, n) + t1 := make([]E2, n) + t2 := make([]E2, n) + + var one E2 + one.SetOne() + + for i := 0; i < n; i++ { + // t0 = g1^2 + t0[i].Square(&x[i].C0.B1) + // t1 = 3 * g1^2 - 2 * g2 + t1[i].Sub(&t0[i], &x[i].C0.B2). + Double(&t1[i]). + Add(&t1[i], &t0[i]) + // t0 = E * g5^2 + t1 + t2[i].Square(&x[i].C1.B2) + t0[i].MulByNonResidue(&t2[i]). + Add(&t0[i], &t1[i]) + // t1 = 4 * g3 + t1[i].Double(&x[i].C1.B0). + Double(&t1[i]) + } + + t1 = BatchInvert(t1) // costs 1 inverse + + for i := 0; i < n; i++ { + // z4 = g4 + x[i].C1.B1.Mul(&t0[i], &t1[i]) + + // t1 = g2 * g1 + t1[i].Mul(&x[i].C0.B2, &x[i].C0.B1) + // t2 = 2 * g4^2 - 3 * g2 * g1 + t2[i].Square(&x[i].C1.B1) + t2[i].Sub(&t2[i], &t1[i]) + t2[i].Double(&t2[i]) + t2[i].Sub(&t2[i], &t1[i]) + + // t1 = g3 * g5 + t1[i].Mul(&x[i].C1.B0, &x[i].C1.B2) + // z0 = E * (2 * g4^2 + g3 * g5 - 3 * g2 * g1) + 1 + t2[i].Add(&t2[i], &t1[i]) + x[i].C0.B0.MulByNonResidue(&t2[i]). + Add(&x[i].C0.B0, &one) + } + + return x +} + +// Granger-Scott's cyclotomic square +// https://eprint.iacr.org/2009/565.pdf, 3.2 func (z *E12) CyclotomicSquare(x *E12) *E12 { // x=(x0,x1,x2,x3,x4,x5,x6,x7) in E2^6 @@ -336,20 +514,19 @@ func (z *E12) SetBytes(e []byte) error { z.C1.B2.A1.SetBytes(e[0 : 0+fp.Bytes]) - // TODO is it the right place? - //if !z.IsInSubGroup() { - // return errors.New("subgroup check failed") - //} - return nil } -var frModulus = fr.Modulus() - // IsInSubGroup ensures GT/E12 is in correct sugroup func (z *E12) IsInSubGroup() bool { - var one, _z E12 - one.SetOne() - _z.Exp(z, *frModulus) - return _z.Equal(&one) + var a, b, _b E12 + + a.Frobenius(z) + b.Expt(z). + Expt(&b). + CyclotomicSquare(&b) + _b.CyclotomicSquare(&b) + b.Mul(&b, &_b) + + return a.Equal(&b) } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12_pairing.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12_pairing.go index 6e7d4d3b752..b29bc3fefcb 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12_pairing.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e12_pairing.go @@ -1,130 +1,186 @@ package fptower -import ( - "math/bits" -) - -// MulByVW set z to x*(y*v*w) and return z -// here y*v*w means the E12 element with C1.B1=y and all other components 0 -func (z *E12) MulByVW(x *E12, y *E2) *E12 { - - var result E12 - var yNR E2 - - yNR.MulByNonResidue(y) - result.C0.B0.Mul(&x.C1.B1, &yNR) - result.C0.B1.Mul(&x.C1.B2, &yNR) - result.C0.B2.Mul(&x.C1.B0, y) - result.C1.B0.Mul(&x.C0.B2, &yNR) - result.C1.B1.Mul(&x.C0.B0, y) - result.C1.B2.Mul(&x.C0.B1, y) - z.Set(&result) - return z -} - -// MulByV set z to x*(y*v) and return z -// here y*v means the E12 element with C0.B1=y and all other components 0 -func (z *E12) MulByV(x *E12, y *E2) *E12 { - - var result E12 - var yNR E2 - - yNR.MulByNonResidue(y) - result.C0.B0.Mul(&x.C0.B2, &yNR) - result.C0.B1.Mul(&x.C0.B0, y) - result.C0.B2.Mul(&x.C0.B1, y) - result.C1.B0.Mul(&x.C1.B2, &yNR) - result.C1.B1.Mul(&x.C1.B0, y) - result.C1.B2.Mul(&x.C1.B1, y) - z.Set(&result) - return z +func (z *E12) nSquare(n int) { + for i := 0; i < n; i++ { + z.CyclotomicSquare(z) + } } -// MulByV2W set z to x*(y*v^2*w) and return z -// here y*v^2*w means the E12 element with C1.B2=y and all other components 0 -func (z *E12) MulByV2W(x *E12, y *E2) *E12 { - - var result E12 - var yNR E2 - - yNR.MulByNonResidue(y) - result.C0.B0.Mul(&x.C1.B0, &yNR) - result.C0.B1.Mul(&x.C1.B1, &yNR) - result.C0.B2.Mul(&x.C1.B2, &yNR) - result.C1.B0.Mul(&x.C0.B1, &yNR) - result.C1.B1.Mul(&x.C0.B2, &yNR) - result.C1.B2.Mul(&x.C0.B0, y) - z.Set(&result) - return z +func (z *E12) nSquareCompressed(n int) { + for i := 0; i < n; i++ { + z.CyclotomicSquareCompressed(z) + } } -// Expt set z to x^t in E12 and return z (t is the generator of the BN curve) +// Expt set z to x^t in E12 and return z (t is the generator of the curve) func (z *E12) Expt(x *E12) *E12 { + // Expt computation is derived from the addition chain: + // + // _10 = 2*1 + // _100 = 2*_10 + // _1000 = 2*_100 + // _10000 = 2*_1000 + // _10001 = 1 + _10000 + // _10011 = _10 + _10001 + // _10100 = 1 + _10011 + // _11001 = _1000 + _10001 + // _100010 = 2*_10001 + // _100111 = _10011 + _10100 + // _101001 = _10 + _100111 + // i27 = (_100010 << 6 + _100 + _11001) << 7 + _11001 + // i44 = (i27 << 8 + _101001 + _10) << 6 + _10001 + // i70 = ((i44 << 8 + _101001) << 6 + _101001) << 10 + // return (_100111 + i70) << 6 + _101001 + _1000 + // + // Operations: 62 squares 17 multiplies + // + // Generated by github.com/mmcloughlin/addchain v0.4.0. - const tAbsVal uint64 = 4965661367192848881 + // Allocate Temporaries. + var result, t0, t1, t2, t3, t4, t5, t6 E12 - var result E12 - result.Set(x) + // Step 1: t3 = x^0x2 + t3.CyclotomicSquare(x) - l := bits.Len64(tAbsVal) - 2 - for i := l; i >= 0; i-- { - result.CyclotomicSquare(&result) - if tAbsVal&(1<= 0; i-- { + if zeroes[i] { + continue + } + res[i].Mul(&res[i], &accumulator) + accumulator.Mul(&accumulator, &a[i]) + } + + return res +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_adx_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_adx_amd64.s index cda60cdbb8d..aabff96ad1a 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_adx_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_adx_amd64.s @@ -330,10 +330,10 @@ TEXT ·negE2(SB), NOSPLIT, $0-16 ORQ R8, AX TESTQ AX, AX JNE l1 - MOVQ AX, 32(DX) - MOVQ AX, 40(DX) - MOVQ AX, 48(DX) - MOVQ AX, 56(DX) + MOVQ AX, 0(DX) + MOVQ AX, 8(DX) + MOVQ AX, 16(DX) + MOVQ AX, 24(DX) JMP l3 l1: diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_amd64.s b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_amd64.s index 48a1035bae4..d0c8e8a3d9c 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_amd64.s +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_amd64.s @@ -330,10 +330,10 @@ TEXT ·negE2(SB), NOSPLIT, $0-16 ORQ R8, AX TESTQ AX, AX JNE l1 - MOVQ AX, 32(DX) - MOVQ AX, 40(DX) - MOVQ AX, 48(DX) - MOVQ AX, 56(DX) + MOVQ AX, 0(DX) + MOVQ AX, 8(DX) + MOVQ AX, 16(DX) + MOVQ AX, 24(DX) JMP l3 l1: diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_bn254_fallback.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_bn254_fallback.go index 8c04804b23d..467a96f77eb 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_bn254_fallback.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_bn254_fallback.go @@ -1,3 +1,4 @@ +//go:build !amd64 // +build !amd64 // Copyright 2020 ConsenSys AG diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_fallback.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_fallback.go index eada4c6b580..0ce4d833347 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_fallback.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e2_fallback.go @@ -1,3 +1,4 @@ +//go:build !amd64 // +build !amd64 // Copyright 2020 ConsenSys Software Inc. diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e6.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e6.go index 5ba35cf0207..adc33ceefd5 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e6.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower/e6.go @@ -123,6 +123,70 @@ func (z *E6) MulByNonResidue(x *E6) *E6 { return z } +// MulByE2 multiplies an element in E6 by an element in E2 +func (z *E6) MulByE2(x *E6, y *E2) *E6 { + var yCopy E2 + yCopy.Set(y) + z.B0.Mul(&x.B0, &yCopy) + z.B1.Mul(&x.B1, &yCopy) + z.B2.Mul(&x.B2, &yCopy) + return z +} + +// MulBy01 multiplication by sparse element (c0,c1,0) +func (z *E6) MulBy01(c0, c1 *E2) *E6 { + + var a, b, tmp, t0, t1, t2 E2 + + a.Mul(&z.B0, c0) + b.Mul(&z.B1, c1) + + tmp.Add(&z.B1, &z.B2) + t0.Mul(c1, &tmp) + t0.Sub(&t0, &b) + t0.MulByNonResidue(&t0) + t0.Add(&t0, &a) + + tmp.Add(&z.B0, &z.B2) + t2.Mul(c0, &tmp) + t2.Sub(&t2, &a) + t2.Add(&t2, &b) + + t1.Add(c0, c1) + tmp.Add(&z.B0, &z.B1) + t1.Mul(&t1, &tmp) + t1.Sub(&t1, &a) + t1.Sub(&t1, &b) + + z.B0.Set(&t0) + z.B1.Set(&t1) + z.B2.Set(&t2) + + return z +} + +// MulBy1 multiplication of E6 by sparse element (0, c1, 0) +func (z *E6) MulBy1(c1 *E2) *E6 { + + var b, tmp, t0, t1 E2 + b.Mul(&z.B1, c1) + + tmp.Add(&z.B1, &z.B2) + t0.Mul(c1, &tmp) + t0.Sub(&t0, &b) + t0.MulByNonResidue(&t0) + + tmp.Add(&z.B0, &z.B1) + t1.Mul(c1, &tmp) + t1.Sub(&t1, &b) + + z.B0.Set(&t0) + z.B1.Set(&t1) + z.B2.Set(&b) + + return z +} + // Mul sets z to the E6 product of x,y, returns z func (z *E6) Mul(x, y *E6) *E6 { // Algorithm 13 from https://eprint.iacr.org/2010/354.pdf diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/marshal.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/marshal.go index 68a0b40523d..08194ed7930 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/marshal.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/marshal.go @@ -14,7 +14,6 @@ // Code generated by consensys/gnark-crypto DO NOT EDIT -// Package bn254 provides efficient elliptic curve and pairing implementation for bn254 package bn254 import ( @@ -54,14 +53,21 @@ type Encoder struct { // Decoder reads bn254 object values from an inbound stream type Decoder struct { - r io.Reader - n int64 // read bytes + r io.Reader + n int64 // read bytes + subGroupCheck bool // default to true } // NewDecoder returns a binary decoder supporting curve bn254 objects in both // compressed and uncompressed (raw) forms -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r: r} +func NewDecoder(r io.Reader, options ...func(*Decoder)) *Decoder { + d := &Decoder{r: r, subGroupCheck: true} + + for _, o := range options { + o(d) + } + + return d } // Decode reads the binary encoding of v from the stream @@ -73,35 +79,69 @@ func (dec *Decoder) Decode(v interface{}) (err error) { } // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap + // in particular, careful attention must be given to usage of Bytes() method on Elements and Points + // that return an array (not a slice) of bytes. Using this is beneficial to minimize memallocs + // in very large (de)serialization upstream in gnark. + // (but detrimental to code lisibility here) // TODO double check memory usage and factorize this var buf [SizeOfG2AffineUncompressed]byte var read int switch t := v.(type) { - case *uint64: - var r uint64 - r, err = dec.readUint64() + case *fr.Element: + read, err = io.ReadFull(dec.r, buf[:fr.Bytes]) + dec.n += int64(read) if err != nil { return } - *t = r + t.SetBytes(buf[:fr.Bytes]) return - case *fr.Element: - read, err = io.ReadFull(dec.r, buf[:fr.Limbs*8]) + case *fp.Element: + read, err = io.ReadFull(dec.r, buf[:fp.Bytes]) dec.n += int64(read) if err != nil { return } - t.SetBytes(buf[:fr.Limbs*8]) + t.SetBytes(buf[:fp.Bytes]) return - case *fp.Element: - read, err = io.ReadFull(dec.r, buf[:fp.Limbs*8]) - dec.n += int64(read) + case *[]fr.Element: + var sliceLen uint32 + sliceLen, err = dec.readUint32() if err != nil { return } - t.SetBytes(buf[:fp.Limbs*8]) + if len(*t) != int(sliceLen) { + *t = make([]fr.Element, sliceLen) + } + + for i := 0; i < len(*t); i++ { + read, err = io.ReadFull(dec.r, buf[:fr.Bytes]) + dec.n += int64(read) + if err != nil { + return + } + (*t)[i].SetBytes(buf[:fr.Bytes]) + } + return + case *[]fp.Element: + var sliceLen uint32 + sliceLen, err = dec.readUint32() + if err != nil { + return + } + if len(*t) != int(sliceLen) { + *t = make([]fp.Element, sliceLen) + } + + for i := 0; i < len(*t); i++ { + read, err = io.ReadFull(dec.r, buf[:fp.Bytes]) + dec.n += int64(read) + if err != nil { + return + } + (*t)[i].SetBytes(buf[:fp.Bytes]) + } return case *G1Affine: // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. @@ -121,7 +161,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) { return } } - _, err = t.SetBytes(buf[:nbBytes]) + _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) return case *G2Affine: // we start by reading compressed point size, if metadata tells us it is uncompressed, we read more. @@ -141,7 +181,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) { return } } - _, err = t.SetBytes(buf[:nbBytes]) + _, err = t.setBytes(buf[:nbBytes], dec.subGroupCheck) return case *[]G1Affine: var sliceLen uint32 @@ -171,7 +211,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) { if err != nil { return } - _, err = (*t)[i].SetBytes(buf[:nbBytes]) + _, err = (*t)[i].setBytes(buf[:nbBytes], false) if err != nil { return } @@ -183,7 +223,11 @@ func (dec *Decoder) Decode(v interface{}) (err error) { parallel.Execute(len(compressed), func(start, end int) { for i := start; i < end; i++ { if compressed[i] { - if err := (*t)[i].unsafeComputeY(); err != nil { + if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { + atomic.AddUint64(&nbErrs, 1) + } + } else if dec.subGroupCheck { + if !(*t)[i].IsInSubGroup() { atomic.AddUint64(&nbErrs, 1) } } @@ -222,7 +266,7 @@ func (dec *Decoder) Decode(v interface{}) (err error) { if err != nil { return } - _, err = (*t)[i].SetBytes(buf[:nbBytes]) + _, err = (*t)[i].setBytes(buf[:nbBytes], false) if err != nil { return } @@ -234,7 +278,11 @@ func (dec *Decoder) Decode(v interface{}) (err error) { parallel.Execute(len(compressed), func(start, end int) { for i := start; i < end; i++ { if compressed[i] { - if err := (*t)[i].unsafeComputeY(); err != nil { + if err := (*t)[i].unsafeComputeY(dec.subGroupCheck); err != nil { + atomic.AddUint64(&nbErrs, 1) + } + } else if dec.subGroupCheck { + if !(*t)[i].IsInSubGroup() { atomic.AddUint64(&nbErrs, 1) } } @@ -246,7 +294,15 @@ func (dec *Decoder) Decode(v interface{}) (err error) { return nil default: - return errors.New("bn254 encoder: unsupported type") + n := binary.Size(t) + if n == -1 { + return errors.New("bn254 encoder: unsupported type") + } + err = binary.Read(dec.r, binary.BigEndian, t) + if err == nil { + dec.n += int64(n) + } + return } } @@ -255,18 +311,6 @@ func (dec *Decoder) BytesRead() int64 { return dec.n } -func (dec *Decoder) readUint64() (r uint64, err error) { - var read int - var buf [8]byte - read, err = io.ReadFull(dec.r, buf[:8]) - dec.n += int64(read) - if err != nil { - return - } - r = binary.BigEndian.Uint64(buf[:8]) - return -} - func (dec *Decoder) readUint32() (r uint32, err error) { var read int var buf [4]byte @@ -323,6 +367,14 @@ func RawEncoding() func(*Encoder) { } } +// NoSubgroupChecks returns an option to use in NewDecoder(...) which disable subgroup checks on the points +// the decoder will read. Use with caution, as crafted points from an untrusted source can lead to crypto-attacks. +func NoSubgroupChecks() func(*Decoder) { + return func(dec *Decoder) { + dec.subGroupCheck = false + } +} + func (enc *Encoder) encode(v interface{}) (err error) { // implementation note: code is a bit verbose (abusing code generation), but minimize allocations on the heap @@ -330,10 +382,6 @@ func (enc *Encoder) encode(v interface{}) (err error) { var written int switch t := v.(type) { - case uint64: - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += 8 - return case *fr.Element: buf := t.Bytes() written, err = enc.w.Write(buf[:]) @@ -354,6 +402,41 @@ func (enc *Encoder) encode(v interface{}) (err error) { written, err = enc.w.Write(buf[:]) enc.n += int64(written) return + case []fr.Element: + // write slice length + err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) + if err != nil { + return + } + enc.n += 4 + var buf [fr.Bytes]byte + for i := 0; i < len(t); i++ { + buf = t[i].Bytes() + written, err = enc.w.Write(buf[:]) + enc.n += int64(written) + if err != nil { + return + } + } + return nil + case []fp.Element: + // write slice length + err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) + if err != nil { + return + } + enc.n += 4 + var buf [fp.Bytes]byte + for i := 0; i < len(t); i++ { + buf = t[i].Bytes() + written, err = enc.w.Write(buf[:]) + enc.n += int64(written) + if err != nil { + return + } + } + return nil + case []G1Affine: // write slice length err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) @@ -393,7 +476,13 @@ func (enc *Encoder) encode(v interface{}) (err error) { } return nil default: - return errors.New(" encoder: unsupported type") + n := binary.Size(t) + if n == -1 { + return errors.New(" encoder: unsupported type") + } + err = binary.Write(enc.w, binary.BigEndian, t) + enc.n += int64(n) + return } } @@ -404,10 +493,6 @@ func (enc *Encoder) encodeRaw(v interface{}) (err error) { var written int switch t := v.(type) { - case uint64: - err = binary.Write(enc.w, binary.BigEndian, t) - enc.n += 8 - return case *fr.Element: buf := t.Bytes() written, err = enc.w.Write(buf[:]) @@ -428,6 +513,41 @@ func (enc *Encoder) encodeRaw(v interface{}) (err error) { written, err = enc.w.Write(buf[:]) enc.n += int64(written) return + case []fr.Element: + // write slice length + err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) + if err != nil { + return + } + enc.n += 4 + var buf [fr.Bytes]byte + for i := 0; i < len(t); i++ { + buf = t[i].Bytes() + written, err = enc.w.Write(buf[:]) + enc.n += int64(written) + if err != nil { + return + } + } + return nil + case []fp.Element: + // write slice length + err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) + if err != nil { + return + } + enc.n += 4 + var buf [fp.Bytes]byte + for i := 0; i < len(t); i++ { + buf = t[i].Bytes() + written, err = enc.w.Write(buf[:]) + enc.n += int64(written) + if err != nil { + return + } + } + return nil + case []G1Affine: // write slice length err = binary.Write(enc.w, binary.BigEndian, uint32(len(t))) @@ -467,7 +587,13 @@ func (enc *Encoder) encodeRaw(v interface{}) (err error) { } return nil default: - return errors.New(" encoder: unsupported type") + n := binary.Size(t) + if n == -1 { + return errors.New(" encoder: unsupported type") + } + err = binary.Write(enc.w, binary.BigEndian, t) + enc.n += int64(n) + return } } @@ -573,6 +699,10 @@ func (p *G1Affine) RawBytes() (res [SizeOfG1AffineUncompressed]byte) { // the Y coordinate (i.e the square root doesn't exist) this function retunrs an error // this check if the resulting point is on the curve and in the correct subgroup func (p *G1Affine) SetBytes(buf []byte) (int, error) { + return p.setBytes(buf, true) +} + +func (p *G1Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { if len(buf) < SizeOfG1AffineCompressed { return 0, io.ErrShortBuffer } @@ -601,7 +731,7 @@ func (p *G1Affine) SetBytes(buf []byte) (int, error) { p.Y.SetBytes(buf[fp.Bytes : fp.Bytes*2]) // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return 0, errors.New("invalid point: subgroup check failed") } @@ -643,7 +773,7 @@ func (p *G1Affine) SetBytes(buf []byte) (int, error) { p.Y = Y // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return 0, errors.New("invalid point: subgroup check failed") } @@ -652,7 +782,7 @@ func (p *G1Affine) SetBytes(buf []byte) (int, error) { // unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) // it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G1Affine) unsafeComputeY() error { +func (p *G1Affine) unsafeComputeY(subGroupCheck bool) error { // stored in unsafeSetCompressedBytes mData := byte(p.Y[0]) @@ -681,7 +811,7 @@ func (p *G1Affine) unsafeComputeY() error { p.Y = Y // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return errors.New("invalid point: subgroup check failed") } @@ -764,7 +894,7 @@ func (p *G2Affine) Bytes() (res [SizeOfG2AffineCompressed]byte) { } // we store X and mask the most significant word with our metadata mask - // p.X.A0 | p.X.A1 + // p.X.A1 | p.X.A0 tmp = p.X.A0 tmp.FromMont() binary.BigEndian.PutUint64(res[56:64], tmp[0]) @@ -801,7 +931,7 @@ func (p *G2Affine) RawBytes() (res [SizeOfG2AffineUncompressed]byte) { // not compressed // we store the Y coordinate - // p.Y.A0 | p.Y.A1 + // p.Y.A1 | p.Y.A0 tmp = p.Y.A0 tmp.FromMont() binary.BigEndian.PutUint64(res[120:128], tmp[0]) @@ -817,7 +947,7 @@ func (p *G2Affine) RawBytes() (res [SizeOfG2AffineUncompressed]byte) { binary.BigEndian.PutUint64(res[64:72], tmp[3]) // we store X and mask the most significant word with our metadata mask - // p.X.A0 | p.X.A1 + // p.X.A1 | p.X.A0 tmp = p.X.A1 tmp.FromMont() binary.BigEndian.PutUint64(res[24:32], tmp[0]) @@ -844,6 +974,10 @@ func (p *G2Affine) RawBytes() (res [SizeOfG2AffineUncompressed]byte) { // the Y coordinate (i.e the square root doesn't exist) this function retunrs an error // this check if the resulting point is on the curve and in the correct subgroup func (p *G2Affine) SetBytes(buf []byte) (int, error) { + return p.setBytes(buf, true) +} + +func (p *G2Affine) setBytes(buf []byte, subGroupCheck bool) (int, error) { if len(buf) < SizeOfG2AffineCompressed { return 0, io.ErrShortBuffer } @@ -868,15 +1002,15 @@ func (p *G2Affine) SetBytes(buf []byte) (int, error) { // uncompressed point if mData == mUncompressed { // read X and Y coordinates - // p.X.A1 | p.X.A0 + // p.X.A1 | p.X.A0 p.X.A1.SetBytes(buf[:fp.Bytes]) p.X.A0.SetBytes(buf[fp.Bytes : fp.Bytes*2]) - // p.Y.A1 | p.Y.A0 + // p.Y.A1 | p.Y.A0 p.Y.A1.SetBytes(buf[fp.Bytes*2 : fp.Bytes*3]) p.Y.A0.SetBytes(buf[fp.Bytes*3 : fp.Bytes*4]) // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return 0, errors.New("invalid point: subgroup check failed") } @@ -893,7 +1027,7 @@ func (p *G2Affine) SetBytes(buf []byte) (int, error) { bufX[0] &= ^mMask // read X coordinate - // p.X.A1 | p.X.A0 + // p.X.A1 | p.X.A0 p.X.A1.SetBytes(bufX[:fp.Bytes]) p.X.A0.SetBytes(buf[fp.Bytes : fp.Bytes*2]) @@ -921,7 +1055,7 @@ func (p *G2Affine) SetBytes(buf []byte) (int, error) { p.Y = Y // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return 0, errors.New("invalid point: subgroup check failed") } @@ -930,7 +1064,7 @@ func (p *G2Affine) SetBytes(buf []byte) (int, error) { // unsafeComputeY called by Decoder when processing slices of compressed point in parallel (step 2) // it computes the Y coordinate from the already set X coordinate and is compute intensive -func (p *G2Affine) unsafeComputeY() error { +func (p *G2Affine) unsafeComputeY(subGroupCheck bool) error { // stored in unsafeSetCompressedBytes mData := byte(p.Y.A0[0]) @@ -960,7 +1094,7 @@ func (p *G2Affine) unsafeComputeY() error { p.Y = Y // subgroup check - if !p.IsInSubGroup() { + if subGroupCheck && !p.IsInSubGroup() { return errors.New("invalid point: subgroup check failed") } @@ -989,7 +1123,7 @@ func (p *G2Affine) unsafeSetCompressedBytes(buf []byte) (isInfinity bool) { bufX[0] &= ^mMask // read X coordinate - // p.X.A1 | p.X.A0 + // p.X.A1 | p.X.A0 p.X.A1.SetBytes(bufX[:fp.Bytes]) p.X.A0.SetBytes(buf[fp.Bytes : fp.Bytes*2]) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/multiexp.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/multiexp.go index 89d958a575d..088fab69145 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/multiexp.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/multiexp.go @@ -17,33 +17,14 @@ package bn254 import ( + "errors" + "github.com/consensys/gnark-crypto/ecc" "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark-crypto/internal/parallel" "math" "runtime" - "sync" ) -// CPUSemaphore enables users to set optional number of CPUs the multiexp will use -// this is thread safe and can be used accross parallel calls of MultiExp -type CPUSemaphore struct { - chCpus chan struct{} // semaphore to limit number of cpus iterating through points and scalrs at the same time - lock sync.Mutex -} - -// NewCPUSemaphore returns a new multiExp options to be used with MultiExp -// this option can be shared between different MultiExp calls and will ensure only numCpus are used -// through a semaphore -func NewCPUSemaphore(numCpus int) *CPUSemaphore { - toReturn := &CPUSemaphore{ - chCpus: make(chan struct{}, numCpus), - } - for i := 0; i < numCpus; i++ { - toReturn.chCpus <- struct{}{} - } - return toReturn -} - // selector stores the index, mask and shifts needed to select bits from a scalar // it is used during the multiExp algorithm or the batch scalar multiplication type selector struct { @@ -61,7 +42,10 @@ type selector struct { // 2^{c} to the current digit, making it negative. // negative digits can be processed in a later step as adding -G into the bucket instead of G // (computing -G is cheap, and this saves us half of the buckets in the MultiExp or BatchScalarMul) -func partitionScalars(scalars []fr.Element, c uint64) []fr.Element { +// scalarsMont indicates wheter the provided scalars are in montgomery form +// returns smallValues, which represent the number of scalars which meets the following condition +// 0 < scalar < 2^c (in other words, scalars where only the c-least significant bits are non zero) +func partitionScalars(scalars []fr.Element, c uint64, scalarsMont bool, nbTasks int) ([]fr.Element, int) { toReturn := make([]fr.Element, len(scalars)) // number of c-bit radixes in a scalar @@ -92,10 +76,36 @@ func partitionScalars(scalars []fr.Element, c uint64) []fr.Element { selectors[chunk] = d } + // for each chunk, we could track the number of non-zeros points we will need to process + // this way, if a chunk has more work to do than others, we can spawn off more go routines + // (at the cost of more buckets allocated) + // a simplified approach is to track the small values where only the first word is set + // if this number represent a significant number of points, then we will split first chunk + // processing in the msm in 2, to ensure all go routines finish at ~same time + // /!\ nbTasks is enough as parallel.Execute is not going to spawn more than nbTasks go routine + // if it does, though, this will deadlocK. + chSmallValues := make(chan int, nbTasks) + parallel.Execute(len(scalars), func(start, end int) { + smallValues := 0 for i := start; i < end; i++ { var carry int + scalar := scalars[i] + if scalarsMont { + scalar.FromMont() + } + if scalar.IsUint64() { + // everything is 0, no need to process this scalar + if scalar[0] == 0 { + continue + } + // low c-bits are 1 in mask + if scalar[0]&mask == scalar[0] { + smallValues++ + } + } + // for each chunk in the scalar, compute the current digit, and an eventual carry for chunk := uint64(0); chunk < nbChunks; chunk++ { s := selectors[chunk] @@ -105,11 +115,16 @@ func partitionScalars(scalars []fr.Element, c uint64) []fr.Element { carry = 0 // digit = value of the c-bit window - digit += int((scalars[i][s.index] & s.mask) >> s.shift) + digit += int((scalar[s.index] & s.mask) >> s.shift) if s.multiWordSelect { // we are selecting bits over 2 words - digit += int(scalars[i][s.index+1]&s.maskHigh) << s.shiftHigh + digit += int(scalar[s.index+1]&s.maskHigh) << s.shiftHigh + } + + // if digit is zero, no impact on result + if digit == 0 { + continue } // if the digit is larger than 2^{c-1}, then, we borrow 2^c from the next window and substract @@ -133,24 +148,32 @@ func partitionScalars(scalars []fr.Element, c uint64) []fr.Element { } } - }) - return toReturn + + chSmallValues <- smallValues + + }, nbTasks) + + // aggregate small values + close(chSmallValues) + smallValues := 0 + for o := range chSmallValues { + smallValues += o + } + return toReturn, smallValues } // MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// optionally, takes as parameter a CPUSemaphore struct -// enabling to set max number of cpus to use -func (p *G1Affine) MultiExp(points []G1Affine, scalars []fr.Element, opts ...*CPUSemaphore) *G1Affine { +func (p *G1Affine) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Affine, error) { var _p G1Jac - _p.MultiExp(points, scalars, opts...) + if _, err := _p.MultiExp(points, scalars, config); err != nil { + return nil, err + } p.FromJacobian(&_p) - return p + return p, nil } // MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// optionally, takes as parameter a CPUSemaphore struct -// enabling to set max number of cpus to use -func (p *G1Jac) MultiExp(points []G1Affine, scalars []fr.Element, opts ...*CPUSemaphore) *G1Jac { +func (p *G1Jac) MultiExp(points []G1Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G1Jac, error) { // note: // each of the msmCX method is the same, except for the c constant it declares // duplicating (through template generation) these methods allows to declare the buckets on the stack @@ -177,98 +200,144 @@ func (p *G1Jac) MultiExp(points []G1Affine, scalars []fr.Element, opts ...*CPUSe // step 3 // reduce the buckets weigthed sums into our result (msmReduceChunk) - var opt *CPUSemaphore - if len(opts) > 0 { - opt = opts[0] - } else { - opt = NewCPUSemaphore(runtime.NumCPU()) + // ensure len(points) == len(scalars) + nbPoints := len(points) + if nbPoints != len(scalars) { + return nil, errors.New("len(points) != len(scalars)") } - var C uint64 - nbPoints := len(points) + // if nbTasks is not set, use all available CPUs + if config.NbTasks <= 0 { + config.NbTasks = runtime.NumCPU() + } - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21} - - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := fr.Limbs * 64 * (nbPoints + (1 << (c))) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c + // here, we compute the best C for nbPoints + // we split recursively until nbChunks(c) >= nbTasks, + bestC := func(nbPoints int) uint64 { + // implemented msmC methods (the c we use must be in this slice) + implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21} + var C uint64 + // approximate cost (in group operations) + // cost = bits/c * (nbPoints + 2^{c}) + // this needs to be verified empirically. + // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results + min := math.MaxFloat64 + for _, c := range implementedCs { + cc := fr.Limbs * 64 * (nbPoints + (1 << (c))) + cost := float64(cc) / float64(c) + if cost < min { + min = cost + C = c + } } + // empirical, needs to be tuned. + // if C > 16 && nbPoints < 1 << 23 { + // C = 16 + // } + return C } - // empirical, needs to be tuned. - // if C > 16 && nbPoints < 1 << 23 { - // C = 16 - // } - - // take all the cpus to ourselves - opt.lock.Lock() + var C uint64 + nbSplits := 1 + nbChunks := 0 + for nbChunks < config.NbTasks { + C = bestC(nbPoints) + nbChunks = int(fr.Limbs * 64 / C) // number of c-bit radixes in a scalar + if (fr.Limbs*64)%C != 0 { + nbChunks++ + } + nbChunks *= nbSplits + if nbChunks < config.NbTasks { + nbSplits <<= 1 + nbPoints >>= 1 + } + } // partition the scalars // note: we do that before the actual chunk processing, as for each c-bit window (starting from LSW) // if it's larger than 2^{c-1}, we have a carry we need to propagate up to the higher window - scalars = partitionScalars(scalars, C) + var smallValues int + scalars, smallValues = partitionScalars(scalars, C, config.ScalarsMont, config.NbTasks) + + // if we have more than 10% of small values, we split the processing of the first chunk in 2 + // we may want to do that in msmInnerG1Jac , but that would incur a cost of looping through all scalars one more time + splitFirstChunk := (float64(smallValues) / float64(len(scalars))) >= 0.1 + + // we have nbSplits intermediate results that we must sum together. + _p := make([]G1Jac, nbSplits-1) + chDone := make(chan int, nbSplits-1) + for i := 0; i < nbSplits-1; i++ { + start := i * nbPoints + end := start + nbPoints + go func(start, end, i int) { + msmInnerG1Jac(&_p[i], int(C), points[start:end], scalars[start:end], splitFirstChunk) + chDone <- i + }(start, end, i) + } - switch C { + msmInnerG1Jac(p, int(C), points[(nbSplits-1)*nbPoints:], scalars[(nbSplits-1)*nbPoints:], splitFirstChunk) + for i := 0; i < nbSplits-1; i++ { + done := <-chDone + p.AddAssign(&_p[done]) + } + close(chDone) + return p, nil +} + +func msmInnerG1Jac(p *G1Jac, c int, points []G1Affine, scalars []fr.Element, splitFirstChunk bool) { + + switch c { case 4: - return p.msmC4(points, scalars, opt) + p.msmC4(points, scalars, splitFirstChunk) case 5: - return p.msmC5(points, scalars, opt) + p.msmC5(points, scalars, splitFirstChunk) case 6: - return p.msmC6(points, scalars, opt) + p.msmC6(points, scalars, splitFirstChunk) case 7: - return p.msmC7(points, scalars, opt) + p.msmC7(points, scalars, splitFirstChunk) case 8: - return p.msmC8(points, scalars, opt) + p.msmC8(points, scalars, splitFirstChunk) case 9: - return p.msmC9(points, scalars, opt) + p.msmC9(points, scalars, splitFirstChunk) case 10: - return p.msmC10(points, scalars, opt) + p.msmC10(points, scalars, splitFirstChunk) case 11: - return p.msmC11(points, scalars, opt) + p.msmC11(points, scalars, splitFirstChunk) case 12: - return p.msmC12(points, scalars, opt) + p.msmC12(points, scalars, splitFirstChunk) case 13: - return p.msmC13(points, scalars, opt) + p.msmC13(points, scalars, splitFirstChunk) case 14: - return p.msmC14(points, scalars, opt) + p.msmC14(points, scalars, splitFirstChunk) case 15: - return p.msmC15(points, scalars, opt) + p.msmC15(points, scalars, splitFirstChunk) case 16: - return p.msmC16(points, scalars, opt) + p.msmC16(points, scalars, splitFirstChunk) case 20: - return p.msmC20(points, scalars, opt) + p.msmC20(points, scalars, splitFirstChunk) case 21: - return p.msmC21(points, scalars, opt) + p.msmC21(points, scalars, splitFirstChunk) case 22: - return p.msmC22(points, scalars, opt) + p.msmC22(points, scalars, splitFirstChunk) default: - panic("unimplemented") + panic("not implemented") } } @@ -349,643 +418,832 @@ func msmProcessChunkG1Affine(chunk uint64, } chRes <- total - close(chRes) + } -func (p *G1Jac) msmC4(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 4 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G1Jac) msmC4(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 4 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance - // for each chunk, spawn a go routine that'll loop through all the scalars + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC5(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 5 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC5(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 5 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC6(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 6 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC6(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 6 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC7(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 7 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC7(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 7 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC8(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 8 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G1Jac) msmC8(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 8 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) - // for each chunk, spawn a go routine that'll loop through all the scalars + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC9(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 9 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC9(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 9 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC10(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 10 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC10(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 10 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC11(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 11 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC11(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 11 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC12(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 12 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC12(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 12 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC13(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 13 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC13(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 13 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC14(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 14 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC14(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 14 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC15(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 15 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC15(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 15 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC16(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 16 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G1Jac) msmC16(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 16 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) - // for each chunk, spawn a go routine that'll loop through all the scalars + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC20(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 20 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC20(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 20 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC21(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 21 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC21(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 21 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } -func (p *G1Jac) msmC22(points []G1Affine, scalars []fr.Element, opt *CPUSemaphore) *G1Jac { - const c = 22 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g1JacExtended +func (p *G1Jac) msmC22(points []G1Affine, scalars []fr.Element, splitFirstChunk bool) *G1Jac { + const ( + c = 22 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g1JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g1JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G1Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g1JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g1JacExtended, points []G1Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g1JacExtended - msmProcessChunkG1Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG1Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G1Affine, scalars []fr.Element, chChunk chan g1JacExtended) { + var buckets [1 << (c - 1)]g1JacExtended + msmProcessChunkG1Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g1JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG1Affine(p, c, chChunks[:]) } // MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// optionally, takes as parameter a CPUSemaphore struct -// enabling to set max number of cpus to use -func (p *G2Affine) MultiExp(points []G2Affine, scalars []fr.Element, opts ...*CPUSemaphore) *G2Affine { +func (p *G2Affine) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Affine, error) { var _p G2Jac - _p.MultiExp(points, scalars, opts...) + if _, err := _p.MultiExp(points, scalars, config); err != nil { + return nil, err + } p.FromJacobian(&_p) - return p + return p, nil } // MultiExp implements section 4 of https://eprint.iacr.org/2012/549.pdf -// optionally, takes as parameter a CPUSemaphore struct -// enabling to set max number of cpus to use -func (p *G2Jac) MultiExp(points []G2Affine, scalars []fr.Element, opts ...*CPUSemaphore) *G2Jac { +func (p *G2Jac) MultiExp(points []G2Affine, scalars []fr.Element, config ecc.MultiExpConfig) (*G2Jac, error) { // note: // each of the msmCX method is the same, except for the c constant it declares // duplicating (through template generation) these methods allows to declare the buckets on the stack @@ -1012,98 +1270,144 @@ func (p *G2Jac) MultiExp(points []G2Affine, scalars []fr.Element, opts ...*CPUSe // step 3 // reduce the buckets weigthed sums into our result (msmReduceChunk) - var opt *CPUSemaphore - if len(opts) > 0 { - opt = opts[0] - } else { - opt = NewCPUSemaphore(runtime.NumCPU()) + // ensure len(points) == len(scalars) + nbPoints := len(points) + if nbPoints != len(scalars) { + return nil, errors.New("len(points) != len(scalars)") } - var C uint64 - nbPoints := len(points) + // if nbTasks is not set, use all available CPUs + if config.NbTasks <= 0 { + config.NbTasks = runtime.NumCPU() + } - // implemented msmC methods (the c we use must be in this slice) - implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21, 22} - - // approximate cost (in group operations) - // cost = bits/c * (nbPoints + 2^{c}) - // this needs to be verified empirically. - // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results - min := math.MaxFloat64 - for _, c := range implementedCs { - cc := fr.Limbs * 64 * (nbPoints + (1 << (c))) - cost := float64(cc) / float64(c) - if cost < min { - min = cost - C = c + // here, we compute the best C for nbPoints + // we split recursively until nbChunks(c) >= nbTasks, + bestC := func(nbPoints int) uint64 { + // implemented msmC methods (the c we use must be in this slice) + implementedCs := []uint64{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21, 22} + var C uint64 + // approximate cost (in group operations) + // cost = bits/c * (nbPoints + 2^{c}) + // this needs to be verified empirically. + // for example, on a MBP 2016, for G2 MultiExp > 8M points, hand picking c gives better results + min := math.MaxFloat64 + for _, c := range implementedCs { + cc := fr.Limbs * 64 * (nbPoints + (1 << (c))) + cost := float64(cc) / float64(c) + if cost < min { + min = cost + C = c + } } + // empirical, needs to be tuned. + // if C > 16 && nbPoints < 1 << 23 { + // C = 16 + // } + return C } - // empirical, needs to be tuned. - // if C > 16 && nbPoints < 1 << 23 { - // C = 16 - // } - - // take all the cpus to ourselves - opt.lock.Lock() + var C uint64 + nbSplits := 1 + nbChunks := 0 + for nbChunks < config.NbTasks { + C = bestC(nbPoints) + nbChunks = int(fr.Limbs * 64 / C) // number of c-bit radixes in a scalar + if (fr.Limbs*64)%C != 0 { + nbChunks++ + } + nbChunks *= nbSplits + if nbChunks < config.NbTasks { + nbSplits <<= 1 + nbPoints >>= 1 + } + } // partition the scalars // note: we do that before the actual chunk processing, as for each c-bit window (starting from LSW) // if it's larger than 2^{c-1}, we have a carry we need to propagate up to the higher window - scalars = partitionScalars(scalars, C) + var smallValues int + scalars, smallValues = partitionScalars(scalars, C, config.ScalarsMont, config.NbTasks) + + // if we have more than 10% of small values, we split the processing of the first chunk in 2 + // we may want to do that in msmInnerG2Jac , but that would incur a cost of looping through all scalars one more time + splitFirstChunk := (float64(smallValues) / float64(len(scalars))) >= 0.1 + + // we have nbSplits intermediate results that we must sum together. + _p := make([]G2Jac, nbSplits-1) + chDone := make(chan int, nbSplits-1) + for i := 0; i < nbSplits-1; i++ { + start := i * nbPoints + end := start + nbPoints + go func(start, end, i int) { + msmInnerG2Jac(&_p[i], int(C), points[start:end], scalars[start:end], splitFirstChunk) + chDone <- i + }(start, end, i) + } + + msmInnerG2Jac(p, int(C), points[(nbSplits-1)*nbPoints:], scalars[(nbSplits-1)*nbPoints:], splitFirstChunk) + for i := 0; i < nbSplits-1; i++ { + done := <-chDone + p.AddAssign(&_p[done]) + } + close(chDone) + return p, nil +} - switch C { +func msmInnerG2Jac(p *G2Jac, c int, points []G2Affine, scalars []fr.Element, splitFirstChunk bool) { + + switch c { case 4: - return p.msmC4(points, scalars, opt) + p.msmC4(points, scalars, splitFirstChunk) case 5: - return p.msmC5(points, scalars, opt) + p.msmC5(points, scalars, splitFirstChunk) case 6: - return p.msmC6(points, scalars, opt) + p.msmC6(points, scalars, splitFirstChunk) case 7: - return p.msmC7(points, scalars, opt) + p.msmC7(points, scalars, splitFirstChunk) case 8: - return p.msmC8(points, scalars, opt) + p.msmC8(points, scalars, splitFirstChunk) case 9: - return p.msmC9(points, scalars, opt) + p.msmC9(points, scalars, splitFirstChunk) case 10: - return p.msmC10(points, scalars, opt) + p.msmC10(points, scalars, splitFirstChunk) case 11: - return p.msmC11(points, scalars, opt) + p.msmC11(points, scalars, splitFirstChunk) case 12: - return p.msmC12(points, scalars, opt) + p.msmC12(points, scalars, splitFirstChunk) case 13: - return p.msmC13(points, scalars, opt) + p.msmC13(points, scalars, splitFirstChunk) case 14: - return p.msmC14(points, scalars, opt) + p.msmC14(points, scalars, splitFirstChunk) case 15: - return p.msmC15(points, scalars, opt) + p.msmC15(points, scalars, splitFirstChunk) case 16: - return p.msmC16(points, scalars, opt) + p.msmC16(points, scalars, splitFirstChunk) case 20: - return p.msmC20(points, scalars, opt) + p.msmC20(points, scalars, splitFirstChunk) case 21: - return p.msmC21(points, scalars, opt) + p.msmC21(points, scalars, splitFirstChunk) case 22: - return p.msmC22(points, scalars, opt) + p.msmC22(points, scalars, splitFirstChunk) default: - panic("unimplemented") + panic("not implemented") } } @@ -1184,625 +1488,816 @@ func msmProcessChunkG2Affine(chunk uint64, } chRes <- total - close(chRes) + } -func (p *G2Jac) msmC4(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 4 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G2Jac) msmC4(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 4 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) - // for each chunk, spawn a go routine that'll loop through all the scalars + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC5(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 5 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC5(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 5 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC6(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 6 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC6(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 6 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC7(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 7 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC7(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 7 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC8(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 8 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G2Jac) msmC8(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 8 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) - // for each chunk, spawn a go routine that'll loop through all the scalars + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC9(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 9 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC9(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 9 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC10(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 10 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC10(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 10 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC11(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 11 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC11(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 11 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC12(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 12 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC12(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 12 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC13(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 13 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC13(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 13 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC14(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 14 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC14(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 14 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC15(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 15 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC15(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 15 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC16(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 16 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar +func (p *G2Jac) msmC16(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 16 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) - // for each chunk, spawn a go routine that'll loop through all the scalars + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel var chChunks [nbChunks]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup - for chunk := nbChunks - 1; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC20(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 20 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC20(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 20 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC21(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 21 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC21(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 21 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } -func (p *G2Jac) msmC22(points []G2Affine, scalars []fr.Element, opt *CPUSemaphore) *G2Jac { - const c = 22 // scalars partitioned into c-bit radixes - const nbChunks = (fr.Limbs * 64 / c) + 1 // number of c-bit radixes in a scalar - - // for each chunk, spawn a go routine that'll loop through all the scalars - var chChunks [nbChunks]chan g2JacExtended +func (p *G2Jac) msmC22(points []G2Affine, scalars []fr.Element, splitFirstChunk bool) *G2Jac { + const ( + c = 22 // scalars partitioned into c-bit radixes + nbChunks = (fr.Limbs * 64 / c) // number of c-bit radixes in a scalar + ) + + // for each chunk, spawn one go routine that'll loop through all the scalars in the + // corresponding bit-window + // note that buckets is an array allocated on the stack (for most sizes of c) and this is + // critical for performance + + // each go routine sends its result in chChunks[i] channel + var chChunks [nbChunks + 1]chan g2JacExtended + for i := 0; i < len(chChunks); i++ { + chChunks[i] = make(chan g2JacExtended, 1) + } - // wait group to wait for all the go routines to start - var wg sync.WaitGroup // c doesn't divide 256, last window is smaller we can allocate less buckets const lastC = (fr.Limbs * 64) - (c * (fr.Limbs * 64 / c)) - chChunks[nbChunks-1] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() + go func(j uint64, points []G2Affine, scalars []fr.Element) { var buckets [1 << (lastC - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(nbChunks-1), chChunks[nbChunks-1], points, scalars) - - for chunk := nbChunks - 2; chunk >= 0; chunk-- { - chChunks[chunk] = make(chan g2JacExtended, 1) - <-opt.chCpus // wait to have a cpu before scheduling - wg.Add(1) - go func(j uint64, chRes chan g2JacExtended, points []G2Affine, scalars []fr.Element) { - wg.Done() - var buckets [1 << (c - 1)]g2JacExtended - msmProcessChunkG2Affine(j, chRes, buckets[:], c, points, scalars) - opt.chCpus <- struct{}{} // release token in the semaphore - }(uint64(chunk), chChunks[chunk], points, scalars) - } - - // wait for all goRoutines to actually start - wg.Wait() - - // all my tasks are scheduled, I can let other func use avaiable tokens in the semaphore - opt.lock.Unlock() + msmProcessChunkG2Affine(j, chChunks[j], buckets[:], c, points, scalars) + }(uint64(nbChunks), points, scalars) + + processChunk := func(j int, points []G2Affine, scalars []fr.Element, chChunk chan g2JacExtended) { + var buckets [1 << (c - 1)]g2JacExtended + msmProcessChunkG2Affine(uint64(j), chChunk, buckets[:], c, points, scalars) + } + + for j := int(nbChunks - 1); j > 0; j-- { + go processChunk(j, points, scalars, chChunks[j]) + } + + if !splitFirstChunk { + go processChunk(0, points, scalars, chChunks[0]) + } else { + chSplit := make(chan g2JacExtended, 2) + split := len(points) / 2 + go processChunk(0, points[:split], scalars[:split], chSplit) + go processChunk(0, points[split:], scalars[split:], chSplit) + go func() { + s1 := <-chSplit + s2 := <-chSplit + close(chSplit) + s1.add(&s2) + chChunks[0] <- s1 + }() + } + return msmReduceChunkG2Affine(p, c, chChunks[:]) } diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/pairing.go b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/pairing.go index c73918f7196..5d81cfd9444 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/bn254/pairing.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/bn254/pairing.go @@ -182,6 +182,8 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { } var Q1, Q2 G2Affine + var l0 lineEvaluation + var tmp GT // cf https://eprint.iacr.org/2010/354.pdf for instance for optimal Ate Pairing for k := 0; k < n; k++ { //Q1 = Frob(Q) @@ -192,17 +194,15 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { Q2.X.MulByNonResidue2Power2(&q[k].X) Q2.Y.MulByNonResidue2Power3(&q[k].Y).Neg(&Q2.Y) - qProj[k].AddMixedStep(&l, &Q1) - // line evaluation - l.r0.MulByElement(&l.r0, &p[k].Y) - l.r1.MulByElement(&l.r1, &p[k].X) - result.MulBy034(&l.r0, &l.r1, &l.r2) + qProj[k].AddMixedStep(&l0, &Q1) + l0.r0.MulByElement(&l0.r0, &p[k].Y) + l0.r1.MulByElement(&l0.r1, &p[k].X) qProj[k].AddMixedStep(&l, &Q2) - // line evaluation l.r0.MulByElement(&l.r0, &p[k].Y) l.r1.MulByElement(&l.r1, &p[k].X) - result.MulBy034(&l.r0, &l.r1, &l.r2) + tmp.Mul034by034(&l.r0, &l.r1, &l.r2, &l0.r0, &l0.r1, &l0.r2) + result.Mul(&result, &tmp) } return result, nil @@ -213,9 +213,9 @@ func MillerLoop(P []G1Affine, Q []G2Affine) (GT, error) { func (p *g2Proj) DoubleStep(evaluations *lineEvaluation) { // get some Element from our pool - var t0, t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2 - t0.Mul(&p.x, &p.y) - A.MulByElement(&t0, &twoInv) + var t1, A, B, C, D, E, EE, F, G, H, I, J, K fptower.E2 + A.Mul(&p.x, &p.y) + A.Halve() B.Square(&p.y) C.Square(&p.z) D.Double(&C). @@ -224,7 +224,7 @@ func (p *g2Proj) DoubleStep(evaluations *lineEvaluation) { F.Double(&E). Add(&F, &E) G.Add(&B, &F) - G.MulByElement(&G, &twoInv) + G.Halve() H.Add(&p.y, &p.z). Square(&H) t1.Add(&B, &C) diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/ecc.go b/vendor/github.com/consensys/gnark-crypto/ecc/ecc.go index f6afa3159c6..7f5e531c030 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/ecc.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/ecc.go @@ -14,23 +14,45 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package ecc is an elliptic curve (+pairing) library. -// Provides implementation for bls12-381, bls12-377, bn254, bw6-761 and their twisted edwards "companion curves" +// Package ecc provides bls12-381, bls12-377, bn254, bw6-761, bls24-315 and bw6-633 elliptic curves implementation (+pairing). +// +// Also +// +// * Multi exponentiation +// * FFT +// * Polynomial commitment schemes +// * MiMC +// * twisted edwards "companion curves" +// * EdDSA (on the "companion" twisted edwards curves) package ecc +import ( + "math/big" + + "github.com/consensys/gnark-crypto/internal/generator/config" +) + +// ID represent a unique ID for a curve +type ID uint16 + // do not modify the order of this enum const ( UNKNOWN ID = iota BN254 BLS12_377 BLS12_381 + BLS24_315 BW6_761 + BW6_633 ) -// ID represent a unique ID for a curve -type ID uint16 +// Implemented return the list of curves fully implemented in gnark-crypto +func Implemented() []ID { + return []ID{BN254, BLS12_377, BLS12_381, BW6_761, BLS24_315} +} func (id ID) String() string { + // TODO link with config.XXX.Name ? switch id { case BLS12_377: return "bls12_377" @@ -40,7 +62,59 @@ func (id ID) String() string { return "bn254" case BW6_761: return "bw6_761" + case BW6_633: + return "bw6_633" + case BLS24_315: + return "bls24_315" + default: + panic("unimplemented ecc ID") + } +} + +// Info returns constants related to a curve +func (id ID) Info() Info { + // note to avoid circular dependency these are hard coded + // values are checked for non regression in code generation + switch id { + case BLS12_377: + return newInfo(&config.BLS12_377) + case BLS12_381: + return newInfo(&config.BLS12_381) + case BN254: + return newInfo(&config.BN254) + case BW6_761: + return newInfo(&config.BW6_761) + case BW6_633: + return newInfo(&config.BW6_633) + case BLS24_315: + return newInfo(&config.BLS24_315) default: panic("unimplemented ecc ID") } } + +func newInfo(c *config.Curve) Info { + return Info{ + Fp: config.Field{ + Bits: c.FpInfo.Bits, + Bytes: c.FpInfo.Bytes, + Modulus: func() *big.Int { return new(big.Int).Set(c.FpInfo.Modulus()) }, + }, + Fr: config.Field{ + Bits: c.FrInfo.Bits, + Bytes: c.FrInfo.Bytes, + Modulus: func() *big.Int { return new(big.Int).Set(c.FrInfo.Modulus()) }, + }, + } +} + +// Info contains constants related to a curve +type Info struct { + Fp, Fr config.Field +} + +// MultiExpConfig enables to set optional configuration attribute to a call to MultiExp +type MultiExpConfig struct { + NbTasks int // go routines to be used in the multiexp. can be larger than num cpus. + ScalarsMont bool // indicates if the scalars are in montgommery form. Default to false. +} diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/ecc.md b/vendor/github.com/consensys/gnark-crypto/ecc/ecc.md index 11aec026988..b394e4d728a 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/ecc.md +++ b/vendor/github.com/consensys/gnark-crypto/ecc/ecc.md @@ -4,10 +4,11 @@ * BN254 (Ethereum) * BLS12-377 (ZEXE) * BW6-761 (EC supporting pairing on BLS12-377 field of definition) +* BLS24-315 +* BW6-633 (EC supporting pairing on BLS24-315 field of definition) ### Twisted edwards curves -Each of these curve has a `twistededwards` sub-package with its companion curve. Also known as [Jubjub](https://z.cash/technology/jubjub/) (BLS12-381) or [Baby-Jubjub](https://iden3-docs.readthedocs.io/en/latest/_downloads/33717d75ab84e11313cc0d8a090b636f/Baby-Jubjub.pdf) (BN254). +Each of these curve has a `twistededwards` sub-package with its companion curve. In particular, BLS12-381 comapnion curve is known as [Jubjub](https://z.cash/technology/jubjub/) and BN254's [Baby-Jubjub](https://iden3-docs.readthedocs.io/en/latest/_downloads/33717d75ab84e11313cc0d8a090b636f/Baby-Jubjub.pdf). They are of particular interest as they allow efficient elliptic curve cryptography inside zkSNARK circuits. - diff --git a/vendor/github.com/consensys/gnark-crypto/ecc/utils.go b/vendor/github.com/consensys/gnark-crypto/ecc/utils.go index 44ecf200c38..aca2d8e3fa9 100644 --- a/vendor/github.com/consensys/gnark-crypto/ecc/utils.go +++ b/vendor/github.com/consensys/gnark-crypto/ecc/utils.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "errors" "math/big" + "math/bits" ) //------------------------------------------------------- @@ -249,3 +250,19 @@ func ExpandMsgXmd(msg, dst []byte, lenInBytes int) ([]byte, error) { } return res, nil } + +// NextPowerOfTwo returns the next power of 2 of n +func NextPowerOfTwo(n uint64) uint64 { + c := bits.OnesCount64(n) + if c == 0 { + return 1 + } + if c == 1 { + return n + } + t := bits.LeadingZeros64(n) + if t == 0 { + panic("next power of 2 overflows uint64") + } + return uint64(1) << (64 - t) +} diff --git a/vendor/github.com/consensys/gnark-crypto/field/field.go b/vendor/github.com/consensys/gnark-crypto/field/field.go new file mode 100644 index 00000000000..5d521f9415a --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/field/field.go @@ -0,0 +1,305 @@ +// Copyright 2020 ConsenSys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package field provides Golang code generation for efficient field arithmetic operations. +package field + +import ( + "errors" + "math/big" + + "github.com/consensys/gnark-crypto/field/internal/addchain" +) + +var ( + errUnsupportedModulus = errors.New("unsupported modulus. goff only works for prime modulus w/ size > 64bits") + errParseModulus = errors.New("can't parse modulus") +) + +// Field precomputed values used in template for code generation of field element APIs +type Field struct { + PackageName string + ElementName string + ModulusBig *big.Int + Modulus string + ModulusHex string + NbWords int + NbBits int + NbWordsLastIndex int + NbWordsIndexesNoZero []int + NbWordsIndexesFull []int + NbWordsIndexesNoLast []int + NbWordsIndexesNoZeroNoLast []int + P20InversionCorrectiveFac []uint64 + P20InversionNbIterations int + Q []uint64 + QInverse []uint64 + QMinusOneHalvedP []uint64 // ((q-1) / 2 ) + 1 + ASM bool + RSquare []uint64 + One []uint64 + LegendreExponent string // big.Int to base16 string + NoCarry bool + NoCarrySquare bool // used if NoCarry is set, but some op may overflow in square optimization + SqrtQ3Mod4 bool + SqrtAtkin bool + SqrtTonelliShanks bool + SqrtE uint64 + SqrtS []uint64 + SqrtAtkinExponent string // big.Int to base16 string + SqrtSMinusOneOver2 string // big.Int to base16 string + SqrtQ3Mod4Exponent string // big.Int to base16 string + SqrtG []uint64 // NonResidue ^ SqrtR (montgomery form) + NonResidue []uint64 // (montgomery form) + LegendreExponentData *addchain.AddChainData + SqrtAtkinExponentData *addchain.AddChainData + SqrtSMinusOneOver2Data *addchain.AddChainData + SqrtQ3Mod4ExponentData *addchain.AddChainData + UseAddChain bool +} + +// NewField returns a data structure with needed information to generate apis for field element +// +// See field/generator package +func NewField(packageName, elementName, modulus string, useAddChain bool) (*Field, error) { + // parse modulus + var bModulus big.Int + if _, ok := bModulus.SetString(modulus, 10); !ok { + return nil, errParseModulus + } + + // field info + F := &Field{ + PackageName: packageName, + ElementName: elementName, + Modulus: modulus, + ModulusHex: bModulus.Text(16), + ModulusBig: new(big.Int).Set(&bModulus), + UseAddChain: useAddChain, + } + // pre compute field constants + F.NbBits = bModulus.BitLen() + F.NbWords = len(bModulus.Bits()) + if F.NbWords < 2 { + return nil, errUnsupportedModulus + } + + F.NbWordsLastIndex = F.NbWords - 1 + + // set q from big int repr + F.Q = toUint64Slice(&bModulus) + _qHalved := big.NewInt(0) + bOne := new(big.Int).SetUint64(1) + _qHalved.Sub(&bModulus, bOne).Rsh(_qHalved, 1).Add(_qHalved, bOne) + F.QMinusOneHalvedP = toUint64Slice(_qHalved, F.NbWords) + + // setting qInverse + _r := big.NewInt(1) + _r.Lsh(_r, uint(F.NbWords)*64) + _rInv := big.NewInt(1) + _qInv := big.NewInt(0) + extendedEuclideanAlgo(_r, &bModulus, _rInv, _qInv) + _qInv.Mod(_qInv, _r) + F.QInverse = toUint64Slice(_qInv, F.NbWords) + + // Pornin20 inversion correction factors + k := 32 // Optimized for 64 bit machines, still works for 32 + + p20InvInnerLoopNbIterations := 2*F.NbBits - 1 + // if constant time inversion then p20InvInnerLoopNbIterations-- (among other changes) + F.P20InversionNbIterations = (p20InvInnerLoopNbIterations-1)/(k-1) + 1 // ⌈ (2 * field size - 1) / (k-1) ⌉ + F.P20InversionNbIterations += F.P20InversionNbIterations % 2 // "round up" to a multiple of 2 + + kLimbs := k * F.NbWords + p20InversionCorrectiveFacPower := kLimbs*6 + F.P20InversionNbIterations*(kLimbs-k+1) + p20InversionCorrectiveFac := big.NewInt(1) + p20InversionCorrectiveFac.Lsh(p20InversionCorrectiveFac, uint(p20InversionCorrectiveFacPower)) + p20InversionCorrectiveFac.Mod(p20InversionCorrectiveFac, &bModulus) + F.P20InversionCorrectiveFac = toUint64Slice(p20InversionCorrectiveFac, F.NbWords) + + // rsquare + _rSquare := big.NewInt(2) + exponent := big.NewInt(int64(F.NbWords) * 64 * 2) + _rSquare.Exp(_rSquare, exponent, &bModulus) + F.RSquare = toUint64Slice(_rSquare, F.NbWords) + + var one big.Int + one.SetUint64(1) + one.Lsh(&one, uint(F.NbWords)*64).Mod(&one, &bModulus) + F.One = toUint64Slice(&one, F.NbWords) + + // indexes (template helpers) + F.NbWordsIndexesFull = make([]int, F.NbWords) + F.NbWordsIndexesNoZero = make([]int, F.NbWords-1) + F.NbWordsIndexesNoLast = make([]int, F.NbWords-1) + F.NbWordsIndexesNoZeroNoLast = make([]int, F.NbWords-2) + for i := 0; i < F.NbWords; i++ { + F.NbWordsIndexesFull[i] = i + if i > 0 { + F.NbWordsIndexesNoZero[i-1] = i + } + if i != F.NbWords-1 { + F.NbWordsIndexesNoLast[i] = i + if i > 0 { + F.NbWordsIndexesNoZeroNoLast[i-1] = i + } + } + } + + // See https://hackmd.io/@gnark/modular_multiplication + // if the last word of the modulus is smaller or equal to B, + // we can simplify the montgomery multiplication + const B = (^uint64(0) >> 1) - 1 + F.NoCarry = (F.Q[len(F.Q)-1] <= B) && F.NbWords <= 12 + const BSquare = ^uint64(0) >> 2 + F.NoCarrySquare = F.Q[len(F.Q)-1] <= BSquare + + // Legendre exponent (p-1)/2 + var legendreExponent big.Int + legendreExponent.SetUint64(1) + legendreExponent.Sub(&bModulus, &legendreExponent) + legendreExponent.Rsh(&legendreExponent, 1) + F.LegendreExponent = legendreExponent.Text(16) + if F.UseAddChain { + F.LegendreExponentData = addchain.GetAddChain(&legendreExponent) + } + + // Sqrt pre computes + var qMod big.Int + qMod.SetUint64(4) + if qMod.Mod(&bModulus, &qMod).Cmp(new(big.Int).SetUint64(3)) == 0 { + // q ≡ 3 (mod 4) + // using z ≡ ± x^((p+1)/4) (mod q) + F.SqrtQ3Mod4 = true + var sqrtExponent big.Int + sqrtExponent.SetUint64(1) + sqrtExponent.Add(&bModulus, &sqrtExponent) + sqrtExponent.Rsh(&sqrtExponent, 2) + F.SqrtQ3Mod4Exponent = sqrtExponent.Text(16) + + // add chain stuff + if F.UseAddChain { + F.SqrtQ3Mod4ExponentData = addchain.GetAddChain(&sqrtExponent) + } + + } else { + // q ≡ 1 (mod 4) + qMod.SetUint64(8) + if qMod.Mod(&bModulus, &qMod).Cmp(new(big.Int).SetUint64(5)) == 0 { + // q ≡ 5 (mod 8) + // use Atkin's algorithm + // see modSqrt5Mod8Prime in math/big/int.go + F.SqrtAtkin = true + e := new(big.Int).Rsh(&bModulus, 3) // e = (q - 5) / 8 + F.SqrtAtkinExponent = e.Text(16) + if F.UseAddChain { + F.SqrtAtkinExponentData = addchain.GetAddChain(e) + } + } else { + // use Tonelli-Shanks + F.SqrtTonelliShanks = true + + // Write q-1 =2ᵉ * s , s odd + var s big.Int + one.SetUint64(1) + s.Sub(&bModulus, &one) + + e := s.TrailingZeroBits() + s.Rsh(&s, e) + F.SqrtE = uint64(e) + F.SqrtS = toUint64Slice(&s) + + // find non residue + var nonResidue big.Int + nonResidue.SetInt64(2) + one.SetUint64(1) + for big.Jacobi(&nonResidue, &bModulus) != -1 { + nonResidue.Add(&nonResidue, &one) + } + + // g = nonresidue ^ s + var g big.Int + g.Exp(&nonResidue, &s, &bModulus) + // store g in montgomery form + g.Lsh(&g, uint(F.NbWords)*64).Mod(&g, &bModulus) + F.SqrtG = toUint64Slice(&g, F.NbWords) + + // store non residue in montgomery form + nonResidue.Lsh(&nonResidue, uint(F.NbWords)*64).Mod(&nonResidue, &bModulus) + F.NonResidue = toUint64Slice(&nonResidue) + + // (s+1) /2 + s.Sub(&s, &one).Rsh(&s, 1) + F.SqrtSMinusOneOver2 = s.Text(16) + + if F.UseAddChain { + F.SqrtSMinusOneOver2Data = addchain.GetAddChain(&s) + } + } + } + + // note: to simplify output files generated, we generated ASM code only for + // moduli that meet the condition F.NoCarry + // asm code generation for moduli with more than 6 words can be optimized further + F.ASM = F.NoCarry && F.NbWords <= 12 + + return F, nil +} + +func toUint64Slice(b *big.Int, nbWords ...int) (s []uint64) { + if len(nbWords) > 0 && nbWords[0] > len(b.Bits()) { + s = make([]uint64, nbWords[0]) + } else { + s = make([]uint64, len(b.Bits())) + } + + for i, v := range b.Bits() { + s[i] = (uint64)(v) + } + return +} + +// https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm +// r > q, modifies rinv and qinv such that rinv.r - qinv.q = 1 +func extendedEuclideanAlgo(r, q, rInv, qInv *big.Int) { + var s1, s2, t1, t2, qi, tmpMuls, riPlusOne, tmpMult, a, b big.Int + t1.SetUint64(1) + rInv.Set(big.NewInt(1)) + qInv.Set(big.NewInt(0)) + a.Set(r) + b.Set(q) + + // r_i+1 = r_i-1 - q_i.r_i + // s_i+1 = s_i-1 - q_i.s_i + // t_i+1 = t_i-1 - q_i.s_i + for b.Sign() > 0 { + qi.Div(&a, &b) + riPlusOne.Mod(&a, &b) + + tmpMuls.Mul(&s1, &qi) + tmpMult.Mul(&t1, &qi) + + s2.Set(&s1) + t2.Set(&t1) + + s1.Sub(rInv, &tmpMuls) + t1.Sub(qInv, &tmpMult) + rInv.Set(&s2) + qInv.Set(&t2) + + a.Set(&b) + b.Set(&riPlusOne) + } + qInv.Neg(qInv) +} diff --git a/vendor/github.com/consensys/gnark-crypto/field/field.md b/vendor/github.com/consensys/gnark-crypto/field/field.md new file mode 100644 index 00000000000..4e9648ceb83 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/field/field.md @@ -0,0 +1,48 @@ + +# Usage + +At the root of your repo: +```bash +go get github.com/consensys/gnark-crypto/field +``` + +then in a `main.go` (that can be called using a `go:generate` workflow): + +``` +generator.GenerateFF(packageName, structName, modulus, destinationPath, false) +``` + +The generated type has an API that's similar with `big.Int` + +Example API signature +```go +// Mul z = x * y mod q +func (z *Element) Mul(x, y *Element) *Element +``` + +and can be used like so: + +```go +var a, b Element +a.SetUint64(2) +b.SetString("984896738") + +a.Mul(a, b) + +a.Sub(a, a) + .Add(a, b) + .Inv(a) + +b.Exp(b, 42) +b.Neg(b) +``` + +### Build tags + +Generates optimized assembly for `amd64` target. + +For the `Mul` operation, using `ADX` instructions and `ADOX/ADCX` result in a significant performance gain. + +The "default" target `amd64` checks if the running architecture supports these instruction, and reverts to generic path if not. This check adds a branch and forces the function to reserve some bytes on the frame to store the argument to call `_mulGeneric` . + +This package outputs code that can be compiled with `amd64_adx` flag which omits this check. Will crash if the platform running the binary doesn't support the `ADX` instructions (roughly, before 2016). \ No newline at end of file diff --git a/vendor/github.com/consensys/gnark-crypto/field/internal/addchain/addchain.go b/vendor/github.com/consensys/gnark-crypto/field/internal/addchain/addchain.go new file mode 100644 index 00000000000..408c26cf759 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/field/internal/addchain/addchain.go @@ -0,0 +1,327 @@ +// Original copyright : +// BSD 3-Clause License + +// Copyright (c) 2019, Michael McLoughlin +// All rights reserved. + +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: + +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. + +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. + +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. + +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Package addchain is derived from github.com/mmcloughlin/addchain internal packages or examples +package addchain + +import ( + "bufio" + "encoding/gob" + "log" + "math/big" + "os" + "path/filepath" + "reflect" + "strings" + "sync" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/acc" + "github.com/mmcloughlin/addchain/acc/ast" + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/acc/pass" + "github.com/mmcloughlin/addchain/acc/printer" + "github.com/mmcloughlin/addchain/alg/ensemble" + "github.com/mmcloughlin/addchain/alg/exec" + "github.com/mmcloughlin/addchain/meta" +) + +// most of these functions are derived from github.com/mmcloughlin/addchain internal packages or examples + +var ( + once sync.Once + addChainDir string + mAddchains map[string]*AddChainData // key is big.Int.Text(16) +) + +// GetAddChain retunrs template data of a short addition chain for given big.Int +func GetAddChain(n *big.Int) *AddChainData { + + // init the cache only once. + once.Do(initCache) + + key := n.Text(16) + if r, ok := mAddchains[key]; ok { + return r + } + + // Default ensemble of algorithms. + algorithms := ensemble.Ensemble() + + // Use parallel executor. + ex := exec.NewParallel() + results := ex.Execute(n, algorithms) + + // Output best result. + best := 0 + for i, r := range results { + if r.Err != nil { + log.Fatal(r.Err) + } + if len(results[i].Program) < len(results[best].Program) { + best = i + } + } + r := results[best] + data := processSearchResult(r.Program, key) + + mAddchains[key] = data + // gob encode + file := filepath.Join(addChainDir, key) + log.Println("saving addchain", file) + f, err := os.Create(file) + if err != nil { + log.Fatal(err) + } + enc := gob.NewEncoder(f) + + if err := enc.Encode(r.Program); err != nil { + _ = f.Close() + log.Fatal(err) + } + _ = f.Close() + + return data +} + +func processSearchResult(_p addchain.Program, n string) *AddChainData { + p, err := acc.Decompile(_p) + if err != nil { + log.Fatal(err) + } + chain, err := acc.Build(p) + if err != nil { + log.Fatal(err) + } + + data, err := prepareAddChainData(chain, n) + if err != nil { + log.Fatal(err) + } + return data +} + +// Data provided to templates. +type AddChainData struct { + // Chain is the addition chain as a list of integers. + Chain addchain.Chain + + // Ops is the complete sequence of addition operations required to compute + // the addition chain. + Ops addchain.Program + + // Script is the condensed representation of the addition chain computation + // in the "addition chain calculator" language. + Script *ast.Chain + + // Program is the intermediate representation of the addition chain + // computation. This representation is likely the most convenient for code + // generation. It contains a sequence of add, double and shift (repeated + // doubling) instructions required to compute the chain. Temporary variable + // allocation has been performed and the list of required temporaries + // populated. + Program *ir.Program + + // Metadata about the addchain project and the specific release parameters. + // Please use this to include a reference or citation back to the addchain + // project in your generated output. + Meta *meta.Properties + + N string // base 16 value of the value +} + +// PrepareData builds input template data for the given addition chain script. +func prepareAddChainData(s *ast.Chain, n string) (*AddChainData, error) { + // Prepare template data. + allocator := pass.Allocator{ + Input: "x", + Output: "z", + Format: "t%d", + } + // Translate to IR. + p, err := acc.Translate(s) + if err != nil { + return nil, err + } + + // Apply processing passes: temporary variable allocation, and computing the + // full addition chain sequence and operations. + if err := pass.Exec(p, allocator, pass.Func(pass.Eval)); err != nil { + return nil, err + } + + return &AddChainData{ + Chain: p.Chain, + Ops: p.Program, + Script: s, + Program: p, + Meta: meta.Meta, + N: n, + }, nil +} + +// Function is a function provided to templates. +type Function struct { + Name string + Description string + Func interface{} +} + +// Signature returns the function signature. +func (f *Function) Signature() string { + return reflect.ValueOf(f.Func).Type().String() +} + +// Functions is the list of functions provided to templates. +var Functions = []*Function{ + { + Name: "add_", + Description: "If the input operation is an `ir.Add` then return it, otherwise return `nil`", + Func: func(op ir.Op) ir.Op { + if a, ok := op.(ir.Add); ok { + return a + } + return nil + }, + }, + { + Name: "double_", + Description: "If the input operation is an `ir.Double` then return it, otherwise return `nil`", + Func: func(op ir.Op) ir.Op { + if d, ok := op.(ir.Double); ok { + return d + } + return nil + }, + }, + { + Name: "shift_", + Description: "If the input operation is an `ir.Shift` then return it, otherwise return `nil`", + Func: func(op ir.Op) ir.Op { + if s, ok := op.(ir.Shift); ok { + return s + } + return nil + }, + }, + { + Name: "inc_", + Description: "Increment an integer", + Func: func(n int) int { return n + 1 }, + }, + { + Name: "format_", + Description: "Formats an addition chain script (`*ast.Chain`) as a string", + Func: printer.String, + }, + { + Name: "split_", + Description: "Calls `strings.Split`", + Func: strings.Split, + }, + { + Name: "join_", + Description: "Calls `strings.Join`", + Func: strings.Join, + }, + { + Name: "lines_", + Description: "Split input string into lines", + Func: func(s string) []string { + var lines []string + scanner := bufio.NewScanner(strings.NewReader(s)) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + return lines + }, + }, + { + Name: "ptr_", + Description: "adds & if it's a value", + Func: func(s *ir.Operand) string { + if s.String() == "x" { + return "&" + } + return "" + }, + }, + { + Name: "last_", + Func: func(x int, a interface{}) bool { + return x == reflect.ValueOf(a).Len()-1 + }, + }, +} + +// to speed up code generation, we cache addchain search results on disk +func initCache() { + mAddchains = make(map[string]*AddChainData) + + // read existing files in addchain directory + path, err := os.Getwd() + if err != nil { + log.Fatal(err) + } + addChainDir = filepath.Join(path, "addchain") + _ = os.Mkdir(addChainDir, 0700) + files, err := os.ReadDir(addChainDir) + if err != nil { + log.Fatal(err) + } + + // preload pre-computed add chains + for _, entry := range files { + if entry.IsDir() { + continue + } + f, err := os.Open(filepath.Join(addChainDir, entry.Name())) + if err != nil { + log.Fatal(err) + } + + // decode the addchain.Program + dec := gob.NewDecoder(f) + var p addchain.Program + err = dec.Decode(&p) + _ = f.Close() + if err != nil { + log.Fatal(err) + } + data := processSearchResult(p, filepath.Base(f.Name())) + log.Println("read", filepath.Base(f.Name())) + + // save the data + mAddchains[filepath.Base(f.Name())] = data + + } + +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-377.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-377.go new file mode 100644 index 00000000000..4416e2a60ea --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-377.go @@ -0,0 +1,29 @@ +package config + +var BLS12_377 = Curve{ + Name: "bls12-377", + CurvePackage: "bls12377", + EnumID: "BLS12_377", + FrModulus: "8444461749428370424248824938781546531375899335154063827935233455917409239041", + FpModulus: "258664426012969094010652733694893533536393512754914660539884262666720468348340822774968888139573360124440321458177", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + }, + G2: Point{ + CoordType: "fptower.E2", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + Projective: true, + }, +} + +func init() { + addCurve(&BLS12_377) + +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-381.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-381.go new file mode 100644 index 00000000000..986b28e472f --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls12-381.go @@ -0,0 +1,29 @@ +package config + +var BLS12_381 = Curve{ + Name: "bls12-381", + CurvePackage: "bls12381", + EnumID: "BLS12_381", + FrModulus: "52435875175126190479447740508185965837690552500527637822603658699938581184513", + FpModulus: "4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + }, + G2: Point{ + CoordType: "fptower.E2", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + Projective: true, + }, +} + +func init() { + addCurve(&BLS12_381) + +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls24-315.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls24-315.go new file mode 100644 index 00000000000..0c07686195f --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bls24-315.go @@ -0,0 +1,29 @@ +package config + +var BLS24_315 = Curve{ + Name: "bls24-315", + CurvePackage: "bls24315", + EnumID: "BLS24_315", + FrModulus: "11502027791375260645628074404575422495959608200132055716665986169834464870401", + FpModulus: "39705142709513438335025689890408969744933502416914749335064285505637884093126342347073617133569", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + }, + G2: Point{ + CoordType: "fptower.E4", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + Projective: true, + }, +} + +func init() { + addCurve(&BLS24_315) + +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bn254.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bn254.go new file mode 100644 index 00000000000..2b43f717a60 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bn254.go @@ -0,0 +1,28 @@ +package config + +var BN254 = Curve{ + Name: "bn254", + CurvePackage: "bn254", + EnumID: "BN254", + FrModulus: "21888242871839275222246405745257275088548364400416034343698204186575808495617", + FpModulus: "21888242871839275222246405745257275088696311157297823662689037894645226208583", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: false, + CRange: defaultCRange(), + }, + G2: Point{ + CoordType: "fptower.E2", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: defaultCRange(), + Projective: true, + }, +} + +func init() { + addCurve(&BN254) +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-633.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-633.go new file mode 100644 index 00000000000..9ff8f9db83e --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-633.go @@ -0,0 +1,28 @@ +package config + +var BW6_633 = Curve{ + Name: "bw6-633", + CurvePackage: "bw6633", + EnumID: "BW6_633", + FrModulus: "39705142709513438335025689890408969744933502416914749335064285505637884093126342347073617133569", + FpModulus: "20494478644167774678813387386538961497669590920908778075528754551012016751717791778743535050360001387419576570244406805463255765034468441182772056330021723098661967429339971741066259394985997", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: true, + CRange: []int{4, 5, 8, 16}, + Projective: true, + }, + G2: Point{ + CoordType: "fp.Element", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: []int{4, 5, 8, 16}, + }, +} + +func init() { + addCurve(&BW6_633) +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-761.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-761.go new file mode 100644 index 00000000000..9f10c63f6d4 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/bw6-761.go @@ -0,0 +1,28 @@ +package config + +var BW6_761 = Curve{ + Name: "bw6-761", + CurvePackage: "bw6761", + EnumID: "BW6_761", + FrModulus: "258664426012969094010652733694893533536393512754914660539884262666720468348340822774968888139573360124440321458177", + FpModulus: "6891450384315732539396789682275657542479668912536150109513790160209623422243491736087683183289411687640864567753786613451161759120554247759349511699125301598951605099378508850372543631423596795951899700429969112842764913119068299", + G1: Point{ + CoordType: "fp.Element", + PointName: "g1", + GLV: true, + CofactorCleaning: true, + CRange: []int{4, 5, 8, 16}, + Projective: true, + }, + G2: Point{ + CoordType: "fp.Element", + PointName: "g2", + GLV: true, + CofactorCleaning: true, + CRange: []int{4, 5, 8, 16}, + }, +} + +func init() { + addCurve(&BW6_761) +} diff --git a/vendor/github.com/consensys/gnark-crypto/internal/generator/config/curve.go b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/curve.go new file mode 100644 index 00000000000..18c079a90f1 --- /dev/null +++ b/vendor/github.com/consensys/gnark-crypto/internal/generator/config/curve.go @@ -0,0 +1,71 @@ +package config + +import ( + "math/big" + + "github.com/consensys/gnark-crypto/field" +) + +// Curve describes parameters of the curve useful for the template +type Curve struct { + Name string + CurvePackage string + Package string // current package being generated + EnumID string + FpModulus string + FrModulus string + + Fp *field.Field + Fr *field.Field + FpUnusedBits int + + FpInfo, FrInfo Field + G1 Point + G2 Point +} + +type Field struct { + Bits int + Bytes int + Modulus func() *big.Int +} + +func (c Curve) Equal(other Curve) bool { + return c.Name == other.Name +} + +type Point struct { + CoordType string + PointName string + GLV bool // scalar mulitplication using GLV + CofactorCleaning bool // flag telling if the Cofactor cleaning is available + CRange []int // multiexp bucket method: generate inner methods (with const arrays) for each c + Projective bool // generate projective coordinates +} + +var Curves []Curve + +func defaultCRange() []int { + // default range for C values in the multiExp + return []int{4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 21, 22} +} + +func addCurve(c *Curve) { + // init FpInfo and FrInfo + c.FpInfo = newFieldInfo(c.FpModulus) + c.FrInfo = newFieldInfo(c.FrModulus) + Curves = append(Curves, *c) +} + +func newFieldInfo(modulus string) Field { + var F Field + var bModulus big.Int + if _, ok := bModulus.SetString(modulus, 10); !ok { + panic("invalid modulus " + modulus) + } + + F.Bits = bModulus.BitLen() + F.Bytes = len(bModulus.Bits()) * 8 + F.Modulus = func() *big.Int { return new(big.Int).Set(&bModulus) } + return F +} diff --git a/vendor/github.com/mmcloughlin/addchain/.gitignore b/vendor/github.com/mmcloughlin/addchain/.gitignore new file mode 100644 index 00000000000..849ddff3b7e --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/vendor/github.com/mmcloughlin/addchain/.golangci.yml b/vendor/github.com/mmcloughlin/addchain/.golangci.yml new file mode 100644 index 00000000000..1a654ae7438 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/.golangci.yml @@ -0,0 +1,39 @@ +linters: + enable-all: true + disable: + - cyclop + - exhaustivestruct + - forbidigo + - funlen + - gochecknoglobals + - gocognit + - goerr113 + - gomnd + - ifshort + - interfacer + - lll + - maligned + - nlreturn + - paralleltest + - prealloc + - predeclared + - revive + - testpackage + - thelper + - wastedassign + - wrapcheck + - wsl + +linters-settings: + gci: + local-prefixes: github.com/mmcloughlin/addchain + +issues: + exclude-use-default: false + exclude: + # gosec: G304: Potential file inclusion via variable. + - G304 + # gosec: G306: Expect WriteFile permissions to be 0600 or less + - G306 + # gosec: G404: Use of weak random number generator + - G404 diff --git a/vendor/github.com/mmcloughlin/addchain/.goreleaser.yml b/vendor/github.com/mmcloughlin/addchain/.goreleaser.yml new file mode 100644 index 00000000000..4c1df74be2f --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/.goreleaser.yml @@ -0,0 +1,28 @@ +before: + hooks: + - go mod tidy +builds: + - main: ./cmd/addchain + env: + - CGO_ENABLED=0 + goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - -s -w + - -X github.com/mmcloughlin/addchain/meta.buildversion={{ .Version }} +archives: + - format_overrides: + - goos: windows + format: zip + files: + - LICENSE* + - CITATION* + - README* +release: + draft: true + prerelease: auto diff --git a/vendor/github.com/mmcloughlin/addchain/.zenodo.json b/vendor/github.com/mmcloughlin/addchain/.zenodo.json new file mode 100644 index 00000000000..4501f173c4a --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/.zenodo.json @@ -0,0 +1,50 @@ +{ + "title": "mmcloughlin/addchain: v0.4.0", + "description": "Cryptographic Addition Chain Generation in Go", + "version": "0.4.0", + "publication_date": "2021-10-30", + "upload_type": "software", + "access_right": "open", + "license": "BSD-3-Clause", + "creators": [ + { + "name": "McLoughlin, Michael Ben", + "orcid": "0000-0003-2347-6258" + } + ], + "related_identifiers": [ + { + "identifier": "https://github.com/mmcloughlin/addchain/tree/v0.4.0", + "relation": "isSupplementTo", + "scheme": "url" + } + ], + "references": [ + "Adamu Muhammad Noma, Abdullah Muhammed, Mohamad Afendee Mohamed and Zuriati Ahmad Zulkarnain. A Review on Heuristics for Addition Chain Problem: Towards Efficient Public Key Cryptosystems. Journal of Computer Science. 2017. https://thescipub.com/pdf/10.3844/jcssp.2017.275.289.pdf", + "Diego F. Aranha, Paulo S. L. M. Barreto, Geovandro C. C. F. Pereira and Jefferson E. Ricardini. A note on high-security general-purpose elliptic curves. Cryptology ePrint Archive, Report 2013/647. 2013. https://eprint.iacr.org/2013/647", + "Bos, Jurjen and Coster, Matthijs. Addition Chain Heuristics. In Advances in Cryptology --- CRYPTO' 89 Proceedings, pages 400--407. 1990. https://link.springer.com/content/pdf/10.1007/0-387-34805-0_37.pdf", + "Martin Otto. Brauer addition-subtraction chains. PhD thesis, Universitat Paderborn. 2001. http://www.martin-otto.de/publications/docs/2001_MartinOtto_Diplom_BrauerAddition-SubtractionChains.pdf", + "F Bergeron, J Berstel, S Brlek and C Duboc. Addition chains using continued fractions. Journal of Algorithms. 1989. http://www-igm.univ-mlv.fr/~berstel/Articles/1989AdditionChainDuboc.pdf", + "Richard E. Crandall. Method and apparatus for public key exchange in a cryptographic system. US Patent 5,159,632. 1992. https://patents.google.com/patent/US5159632A", + "Bernstein, Daniel J. Curve25519: New Diffie-Hellman Speed Records. In Public Key Cryptography - PKC 2006, pages 207--228. 2006. https://cr.yp.to/ecdh/curve25519-20060209.pdf", + "Brian Smith. The Most Efficient Known Addition Chains for Field Element and Scalar Inversion for the Most Popular and Most Unpopular Elliptic Curves. 2017. https://briansmith.org/ecc-inversion-addition-chains-01 (accessed June 30, 2019)", + "Bergeron, F., Berstel, J. and Brlek, S. Efficient computation of addition chains. Journal de theorie des nombres de Bordeaux. 1994. http://www.numdam.org/item/JTNB_1994__6_1_21_0", + "Daniel J. Bernstein, Mike Hamburg, Anna Krasnova and Tanja Lange. Elligator: Elliptic-curve points indistinguishable from uniform random strings. Cryptology ePrint Archive, Report 2013/325. 2013. https://eprint.iacr.org/2013/325", + "NIST. Digital Signature Standard (DSS). Federal Information Processing Standards Publication 186-2. 2000. https://csrc.nist.gov/csrc/media/publications/fips/186/2/archive/2000-01-27/documents/fips186-2.pdf", + "Amadou Tall and Ali Yassin Sanghare. Efficient computation of addition-subtraction chains using generalized continued Fractions. Cryptology ePrint Archive, Report 2013/466. 2013. https://eprint.iacr.org/2013/466", + "Kunihiro, Noboru and Yamamoto, Hirosuke. New Methods for Generating Short Addition Chains. IEICE Transactions on Fundamentals of Electronics Communications and Computer Sciences. 2000. https://pdfs.semanticscholar.org/b398/d10faca35af9ce5a6026458b251fd0a5640c.pdf", + "Riad S. Wahby. kwantam/addchain. Github Repository. Apache License, Version 2.0. 2018. https://github.com/kwantam/addchain", + "Mike Hamburg. Ed448-Goldilocks, a new elliptic curve. Cryptology ePrint Archive, Report 2015/625. 2015. https://eprint.iacr.org/2015/625", + "Riad S. Wahby and Dan Boneh. Fast and simple constant-time hashing to the BLS12-381 elliptic curve. Cryptology ePrint Archive, Report 2019/403. 2019. https://eprint.iacr.org/2019/403", + "Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve Cryptography, chapter 9. 2006. http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf", + "Knuth, Donald E. Evaluation of Powers. The Art of Computer Programming, Volume 2 (Third Edition): Seminumerical Algorithms, chapter 4.6.3. 1997. https://www-cs-faculty.stanford.edu/~knuth/taocp.html", + "Ayan Nandy. Modifications of Bos and Coster’s Heuristics in search of a shorter addition chain for faster exponentiation. Masters thesis, Indian Statistical Institute Kolkata. 2011. http://library.isical.ac.in:8080/jspui/bitstream/10263/6441/1/DISS-285.pdf", + "F. L. Ţiplea, S. Iftene, C. Hriţcu, I. Goriac, R. Gordân and E. Erbiceanu. MpNT: A Multi-Precision Number Theory Package, Number Theoretical Algorithms (I). Technical Report TR03-02, Faculty of Computer Science, \"Alexandru Ioan Cuza\" University, Iasi. 2003. https://profs.info.uaic.ro/~tr/tr03-02.pdf", + "Daniel J. Bernstein and Tanja Lange. Security dangers of the NIST curves. 2013. https://cr.yp.to/talks/2013.09.16/slides-djb-20130916-a4.pdf", + "Michael Scott, Naomi Benger, Manuel Charlemagne, Luis J. Dominguez Perez and Ezekiel J. Kachisa. On the final exponentiation for calculating pairings on ordinary elliptic curves. Cryptology ePrint Archive, Report 2008/490. 2008. https://eprint.iacr.org/2008/490", + "Daniel J. Bernstein and Tanja Lange. SafeCurves: choosing safe curves for elliptic-curve cryptography. https://safecurves.cr.yp.to", + "Certicom Research. SEC 2: Recommended Elliptic Curve Domain Parameters, Version 2.0. Standards for Efficient Cryptography 2. 2010. https://safecurves.cr.yp.to/www.secg.org/sec2-v2.pdf", + "Jerome A. Solinas. Generalized Mersenne Primes. Technical Report CORR 99-39, Centre for Applied Cryptographic Research (CACR) at the University of Waterloo. 1999. http://cacr.uwaterloo.ca/techreports/1999/corr99-39.pdf", + "Stam, Martijn. Speeding up subgroup cryptosystems. PhD thesis, Technische Universiteit Eindhoven. 2003. https://cr.yp.to/bib/2003/stam-thesis.pdf" + ] +} diff --git a/vendor/github.com/mmcloughlin/addchain/CITATION.bib b/vendor/github.com/mmcloughlin/addchain/CITATION.bib new file mode 100644 index 00000000000..4bddef61f5d --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/CITATION.bib @@ -0,0 +1,11 @@ +@misc{addchain, + title = {addchain: Cryptographic Addition Chain Generation in Go}, + author = {Michael B. McLoughlin}, + year = 2021, + month = oct, + howpublished = {Repository \url{https://github.com/mmcloughlin/addchain}}, + version = {0.4.0}, + license = {BSD 3-Clause License}, + doi = {10.5281/zenodo.5622943}, + url = {https://doi.org/10.5281/zenodo.5622943}, +} diff --git a/vendor/github.com/mmcloughlin/addchain/CITATION.cff b/vendor/github.com/mmcloughlin/addchain/CITATION.cff new file mode 100644 index 00000000000..252327d5ee3 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/CITATION.cff @@ -0,0 +1,19 @@ +cff-version: 1.2.0 +message: "If you use addchain in your work, a citation would be appreciated using the following metadata." +title: "addchain: Cryptographic Addition Chain Generation in Go" +authors: + - family-names: "McLoughlin" + given-names: "Michael Ben" + orcid: "https://orcid.org/0000-0003-2347-6258" +version: "0.4.0" +date-released: "2021-10-30" +license: BSD-3-Clause +repository-code: https://github.com/mmcloughlin/addchain +doi: "10.5281/zenodo.5622943" +identifiers: + - type: doi + value: "10.5281/zenodo.4625263" + description: "The concept DOI of the work." + - type: doi + value: "10.5281/zenodo.5622943" + description: "The versioned DOI for version 0.4.0 of the work." diff --git a/vendor/github.com/mmcloughlin/addchain/LICENSE b/vendor/github.com/mmcloughlin/addchain/LICENSE new file mode 100644 index 00000000000..bc3d0d51028 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, Michael McLoughlin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/mmcloughlin/addchain/README.md b/vendor/github.com/mmcloughlin/addchain/README.md new file mode 100644 index 00000000000..6a1664df09f --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/README.md @@ -0,0 +1,402 @@ +

+ addchain +
+ Build Status + go.dev + Go Report Card + DOI: 10.5281/zenodo.5622943 +

+ +

Cryptographic Addition Chain Generation in Go

+ +`addchain` generates short addition chains for exponents of cryptographic +interest with [results](#results) rivaling the best hand-optimized chains. +Intended as a building block in elliptic curve or other cryptographic code +generators. + +* Suite of algorithms from academic research: continued fractions, + dictionary-based and Bos-Coster heuristics +* Custom run-length techniques exploit structure of cryptographic exponents + with excellent results on Solinas primes +* Generic optimization methods eliminate redundant operations +* Simple domain-specific language for addition chain computations +* Command-line interface or library +* Code generation and templated output support + +## Table of Contents + +* [Background](#background) +* [Results](#results) +* [Usage](#usage) + * [Command-line Interface](#command-line-interface) + * [Library](#library) +* [Algorithms](#algorithms) + * [Binary](#binary) + * [Continued Fractions](#continued-fractions) + * [Bos-Coster Heuristics](#bos-coster-heuristics) + * [Dictionary](#dictionary) + * [Runs](#runs) + * [Optimization](#optimization) +* [Citing](#citing) +* [Thanks](#thanks) +* [Contributing](#contributing) +* [License](#license) + + +## Background + +An [_addition chain_](https://en.wikipedia.org/wiki/Addition_chain) for a +target integer _n_ is a sequence of numbers starting at 1 and ending at _n_ +such that every term is a sum of two numbers appearing earlier in the +sequence. For example, an addition chain for 29 is + +``` +1, 2, 4, 8, 9, 17, 25, 29 +``` + +Addition chains arise in the optimization of exponentiation algorithms with +fixed exponents. For example, the addition chain above corresponds to the +following sequence of multiplications to compute x29 + +
+ x2 = x1 * x1
+ x4 = x2 * x2
+ x8 = x4 * x4
+ x9 = x1 * x8
+x17 = x8 * x9
+x25 = x8 * x17
+x29 = x4 * x25
+
+ +An exponentiation algorithm for a fixed exponent _n_ reduces to finding a +_minimal length addition chain_ for _n_. This is especially relevent in +cryptography where exponentiation by huge fixed exponents forms a +performance-critical component of finite-field arithmetic. In particular, +constant-time inversion modulo a prime _p_ is performed by computing +xp-2 (mod p), thanks to [Fermat's Little +Theorem](https://en.wikipedia.org/wiki/Fermat%27_little_theorem). Square root +also reduces to exponentiation for some prime moduli. Finding short addition +chains for these exponents is one important part of high-performance finite +field implementations required for elliptic curve cryptography or RSA. + +Minimal addition chain search is famously hard. No practical optimal +algorithm is known, especially for cryptographic exponents of size 256-bits +and up. Given its importance for the performance of cryptographic +implementations, implementers devote significant effort to hand-tune addition +chains. The goal of the `addchain` project is to match or exceed the best +hand-optimized addition chains using entirely automated approaches, building +on extensive academic research and applying new tweaks that exploit the +unique nature of cryptographic exponents. + +## Results + +The following table shows the results of the `addchain` library on popular +cryptographic exponents. For each one we also show the length of the [best +known hand-optimized addition chain](https://briansmith.org/ecc-inversion-addition-chains-01), and the +delta from the library result. + +| Name | This Library | Best Known | Delta | +| ---- | -----------: | ---------: | ----: | +| [Curve25519 Field Inversion](doc/results.md#curve25519-field-inversion) | 266 | 265 | +1 | +| [NIST P-256 Field Inversion](doc/results.md#nist-p-256-field-inversion) | 266 | 266 | **+0** | +| [NIST P-384 Field Inversion](doc/results.md#nist-p-384-field-inversion) | 397 | 396 | +1 | +| [secp256k1 (Bitcoin) Field Inversion](doc/results.md#secp256k1-bitcoin-field-inversion) | 269 | 269 | **+0** | +| [Curve25519 Scalar Inversion](doc/results.md#curve25519-scalar-inversion) | 283 | 284 | **-1** | +| [NIST P-256 Scalar Inversion](doc/results.md#nist-p-256-scalar-inversion) | 294 | 292 | +2 | +| [NIST P-384 Scalar Inversion](doc/results.md#nist-p-384-scalar-inversion) | 434 | 433 | +1 | +| [secp256k1 (Bitcoin) Scalar Inversion](doc/results.md#secp256k1-bitcoin-scalar-inversion) | 293 | 290 | +3 | + + +See [full results listing](doc/results.md) for more detail and +results for less common exponents. + +These results demonstrate that `addchain` is competitive with hand-optimized +chains, often with equivalent or better performance. Even when `addchain` is +slightly sub-optimal, it can still be considered valuable since it fully +automates a laborious manual process. As such, `addchain` can be trusted to +produce high quality results in an automated code generation tool. + +## Usage + +### Command-line Interface + +Install a pre-compiled [release +binary](https://github.com/mmcloughlin/addchain/releases): + +``` +curl -sSfL https://git.io/addchain | sh -s -- -b /usr/local/bin +``` + +Alternatively build from source: + +``` +go install github.com/mmcloughlin/addchain/cmd/addchain@latest +``` + +Search for a curve25519 field inversion addition chain with: + +```sh +addchain search '2^255 - 19 - 2' +``` + +Output: + +``` +addchain: expr: "2^255 - 19 - 2" +addchain: hex: 7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb +addchain: dec: 57896044618658097711785492504343953926634992332820282019728792003956564819947 +addchain: best: opt(runs(continued_fractions(dichotomic))) +addchain: cost: 266 +_10 = 2*1 +_11 = 1 + _10 +_1100 = _11 << 2 +_1111 = _11 + _1100 +_11110000 = _1111 << 4 +_11111111 = _1111 + _11110000 +x10 = _11111111 << 2 + _11 +x20 = x10 << 10 + x10 +x30 = x20 << 10 + x10 +x60 = x30 << 30 + x30 +x120 = x60 << 60 + x60 +x240 = x120 << 120 + x120 +x250 = x240 << 10 + x10 +return (x250 << 2 + 1) << 3 + _11 +``` + +Next, you can [generate code from this addition chain](doc/gen.md). + +### Library + +Install: + +``` +go get -u github.com/mmcloughlin/addchain +``` + +Algorithms all conform to the [`alg.ChainAlgorithm`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg#ChainAlgorithm) or +[`alg.SequenceAlgorithm`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg#SequenceAlgorithm) interfaces and can be used directly. However the +most user-friendly method uses the [`alg/ensemble`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/ensemble) package to +instantiate a sensible default set of algorithms and the [`alg/exec`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/exec) +helper to execute them in parallel. The following code uses this method to +find an addition chain for curve25519 field inversion: + +```go +func Example() { + // Target number: 2²⁵⁵ - 21. + n := new(big.Int) + n.SetString("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeb", 16) + + // Default ensemble of algorithms. + algorithms := ensemble.Ensemble() + + // Use parallel executor. + ex := exec.NewParallel() + results := ex.Execute(n, algorithms) + + // Output best result. + best := 0 + for i, r := range results { + if r.Err != nil { + log.Fatal(r.Err) + } + if len(results[i].Program) < len(results[best].Program) { + best = i + } + } + r := results[best] + fmt.Printf("best: %d\n", len(r.Program)) + fmt.Printf("algorithm: %s\n", r.Algorithm) + + // Output: + // best: 266 + // algorithm: opt(runs(continued_fractions(dichotomic))) +} +``` + +## Algorithms + +This section summarizes the algorithms implemented by `addchain` along with +references to primary literature. See the [bibliography](doc/bibliography.md) +for the complete references list. + +### Binary + +The [`alg/binary`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/binary) package implements the addition chain equivalent +of the basic [square-and-multiply exponentiation +method](https://en.wikipedia.org/wiki/Exponentiation_by_squaring). It is +included for completeness, but is almost always outperformed by more advanced +algorithms below. + +### Continued Fractions + +The [`alg/contfrac`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/contfrac) package implements the continued fractions +methods for addition sequence search introduced by +Bergeron-Berstel-Brlek-Duboc in 1989 and later extended. This approach +utilizes a decomposition of an addition chain akin to continued fractions, +namely + +``` +(1,..., k,..., n) = (1,...,n mod k,..., k) ⊗ (1,..., n/k) ⊕ (n mod k). +``` + +for certain special operators ⊗ and ⊕. This +decomposition lends itself to a recursive algorithm for efficient addition +sequence search, with results dependent on the _strategy_ for choosing the +auxillary integer _k_. The [`alg/contfrac`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/contfrac) package provides a +laundry list of strategies from the literature: binary, co-binary, +dichotomic, dyadic, fermat, square-root and total. + +#### References + +* F Bergeron, J Berstel, S Brlek and C Duboc. Addition chains using continued fractions. Journal of Algorithms. 1989. http://www-igm.univ-mlv.fr/~berstel/Articles/1989AdditionChainDuboc.pdf +* Bergeron, F., Berstel, J. and Brlek, S. Efficient computation of addition chains. Journal de theorie des nombres de Bordeaux. 1994. http://www.numdam.org/item/JTNB_1994__6_1_21_0 +* Amadou Tall and Ali Yassin Sanghare. Efficient computation of addition-subtraction chains using generalized continued Fractions. Cryptology ePrint Archive, Report 2013/466. 2013. https://eprint.iacr.org/2013/466 +* Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve Cryptography, chapter 9. 2006. http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + +### Bos-Coster Heuristics + +Bos and Coster described an iterative algorithm for efficient addition +sequence generation in which at each step a heuristic proposes new numbers +for the sequence in such a way that the _maximum_ number always decreases. +The [original Bos-Coster paper](https://link.springer.com/content/pdf/10.1007/0-387-34805-0_37.pdf) defined four +heuristics: Approximation, Divison, Halving and Lucas. Package +[`alg/heuristic`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/heuristic) implements a variation on these heuristics: + +* **Approximation:** looks for two elements a, b in the current sequence with sum close to the largest element. +* **Halving:** applies when the target is at least twice as big as the next largest, and if so it will propose adding a sequence of doublings. +* **Delta Largest:** proposes adding the delta between the largest two entries in the current sequence. + +Divison and Lucas are not implemented due to disparities in the literature +about their precise definition and poor results from early experiments. +Furthermore, this library does not apply weights to the heuristics as +suggested in the paper, rather it simply uses the first that applies. However +both of these remain [possible avenues for +improvement](https://github.com/mmcloughlin/addchain/issues/26). + +#### References + +* Bos, Jurjen and Coster, Matthijs. Addition Chain Heuristics. In Advances in Cryptology --- CRYPTO' 89 Proceedings, pages 400--407. 1990. https://link.springer.com/content/pdf/10.1007/0-387-34805-0_37.pdf +* Riad S. Wahby. kwantam/addchain. Github Repository. Apache License, Version 2.0. 2018. https://github.com/kwantam/addchain +* Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve Cryptography, chapter 9. 2006. http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf +* Ayan Nandy. Modifications of Bos and Coster’s Heuristics in search of a shorter addition chain for faster exponentiation. Masters thesis, Indian Statistical Institute Kolkata. 2011. http://library.isical.ac.in:8080/jspui/bitstream/10263/6441/1/DISS-285.pdf +* F. L. Ţiplea, S. Iftene, C. Hriţcu, I. Goriac, R. Gordân and E. Erbiceanu. MpNT: A Multi-Precision Number Theory Package, Number Theoretical Algorithms (I). Technical Report TR03-02, Faculty of Computer Science, "Alexandru Ioan Cuza" University, Iasi. 2003. https://profs.info.uaic.ro/~tr/tr03-02.pdf +* Stam, Martijn. Speeding up subgroup cryptosystems. PhD thesis, Technische Universiteit Eindhoven. 2003. https://cr.yp.to/bib/2003/stam-thesis.pdf + +### Dictionary + +Dictionary methods decompose the binary representation of a target integer _n_ into a set of dictionary _terms_, such that _n_ +may be written as a sum + +
+n = ∑ 2ei di
+
+ +for exponents _e_ and elements _d_ from a dictionary _D_. Given such a decomposition we can construct an addition chain for _n_ by + +1. Find a short addition _sequence_ containing every element of the dictionary _D_. Continued fractions and Bos-Coster heuristics can be used here. +2. Build _n_ from the dictionary terms according to the sum decomposition. + +The efficiency of this approach boils down to the decomposition method. The [`alg/dict`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/dict) package provides: + +* **Fixed Window:** binary representation of _n_ is broken into fixed _k_-bit windows +* **Sliding Window**: break _n_ into _k_-bit windows, skipping zeros where possible +* **Run Length**: decompose _n_ into runs of 1s up to a maximal length +* **Hybrid**: mix of sliding window and run length methods + +#### References + +* Martin Otto. Brauer addition-subtraction chains. PhD thesis, Universitat Paderborn. 2001. http://www.martin-otto.de/publications/docs/2001_MartinOtto_Diplom_BrauerAddition-SubtractionChains.pdf +* Kunihiro, Noboru and Yamamoto, Hirosuke. New Methods for Generating Short Addition Chains. IEICE Transactions on Fundamentals of Electronics Communications and Computer Sciences. 2000. https://pdfs.semanticscholar.org/b398/d10faca35af9ce5a6026458b251fd0a5640c.pdf +* Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve Cryptography, chapter 9. 2006. http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + +### Runs + +The runs algorithm is a custom variant of the dictionary approach that +decomposes a target into runs of ones. It leverages the observation that +building a dictionary consisting of runs of 1s of lengths +l1, l2, ..., lk can itself be +reduced to: + +1. Find an addition sequence containing the run lengths + li. As with dictionary approaches we can use + Bos-Coster heuristics and continued fractions here. However here we have the + advantage that the li are typically very _small_, + meaning that a wider range of algorithms can be brought to bear. +2. Use the addition sequence for the run lengths li + to build an addition sequence for the runs themselves + r(li) where r(e) = 2e-1. See + [`dict.RunsChain`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/dict#RunsChain). + +This approach has proved highly effective against cryptographic exponents +which frequently exhibit binary structure, such as those derived from +[Solinas primes](https://en.wikipedia.org/wiki/Solinas_prime). + +> I have not seen this method discussed in the literature. Please help me find references to prior art if you know any. + +### Optimization + +Close inspection of addition chains produced by other algorithms revealed +cases of redundant computation. This motivated a final optimization pass over +addition chains to remove unecessary steps. The [`alg/opt`](https://pkg.go.dev/github.com/mmcloughlin/addchain/alg/opt) package +implements the following optimization: + +1. Determine _all possible_ ways each element can be computed from those prior. +2. Count how many times each element is used where it is the _only possible_ way of computing that entry. +3. Prune elements that are always used in computations that have an alternative. + +These micro-optimizations were vital in closing the gap between `addchain`'s +automated approaches and hand-optimized chains. This technique is reminiscent +of basic passes in optimizing compilers, raising the question of whether +other [compiler optimizations could apply to addition +chains](https://github.com/mmcloughlin/addchain/issues/24)? + +> I have not seen this method discussed in the literature. Please help me find references to prior art if you know any. + +## Citing + +If you use `addchain` in your research a citation would be appreciated. +Citing a specific release is preferred, since they are [archived on +Zenodo](https://doi.org/10.5281/zenodo.4625263) and assigned a DOI. Please use the +following BibTeX to cite the most recent [0.4.0 +release](https://github.com/mmcloughlin/addchain/releases/tag/v0.4.0). + +```bib +@misc{addchain, + title = {addchain: Cryptographic Addition Chain Generation in Go}, + author = {Michael B. McLoughlin}, + year = 2021, + month = oct, + howpublished = {Repository \url{https://github.com/mmcloughlin/addchain}}, + version = {0.4.0}, + license = {BSD 3-Clause License}, + doi = {10.5281/zenodo.5622943}, + url = {https://doi.org/10.5281/zenodo.5622943}, +} +``` + +If you need to cite a currently unreleased version please consider [filing an +issue](https://github.com/mmcloughlin/addchain/issues/new) to request a new +release, or to discuss an appropriate format for the citation. + +## Thanks + +Thank you to [Tom Dean](https://web.stanford.edu/~trdean/), [Riad +Wahby](https://wahby.org/), [Brian Smith](https://briansmith.org/) and +[str4d](https://github.com/str4d) for advice and encouragement. Thanks also to +[Damian Gryski](https://github.com/dgryski) and [Martin +Glancy](https://twitter.com/mglancy) for review. + +## Contributing + +Contributions to `addchain` are welcome: + +* [Submit bug reports](https://github.com/mmcloughlin/addchain/issues/new) to + the issues page. +* Suggest [test cases](https://github.com/mmcloughlin/addchain/blob/e6c070065205efcaa02627ab1b23e8ce6aeea1db/internal/results/results.go#L62) + or update best-known hand-optimized results. +* Pull requests accepted. Please discuss in the [issues section](https://github.com/mmcloughlin/addchain/issues) + before starting significant work. + +## License + +`addchain` is available under the [BSD 3-Clause License](LICENSE). diff --git a/vendor/github.com/mmcloughlin/addchain/acc/acc.go b/vendor/github.com/mmcloughlin/addchain/acc/acc.go new file mode 100644 index 00000000000..6963ff44368 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/acc.go @@ -0,0 +1,91 @@ +// Package acc implements the "addition chain calculator" language: a +// domain-specific language (DSL) for addition chain computation. +package acc + +import ( + "bytes" + "io" + "os" + "strings" + + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/acc/parse" + "github.com/mmcloughlin/addchain/acc/pass" + "github.com/mmcloughlin/addchain/acc/printer" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// LoadFile is a convenience for loading an addition chain script from a file. +func LoadFile(filename string) (p *ir.Program, err error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer errutil.CheckClose(&err, f) + return LoadReader(filename, f) +} + +// LoadString is a convenience for loading and evaluating an addition chain +// script from a string. +func LoadString(src string) (*ir.Program, error) { + return LoadReader("string", strings.NewReader(src)) +} + +// LoadReader is a convenience for loading and evaluating an addition chain +// script. +func LoadReader(filename string, r io.Reader) (*ir.Program, error) { + // Parse to AST. + s, err := parse.Reader(filename, r) + if err != nil { + return nil, err + } + + // Translate to IR. + p, err := Translate(s) + if err != nil { + return nil, err + } + + // Evaluate the program. + if err := pass.Eval(p); err != nil { + return nil, err + } + + return p, nil +} + +// Write is a convenience for writing a program as an addition chain script. +func Write(w io.Writer, p *ir.Program) error { + // Build AST. + s, err := Build(p) + if err != nil { + return err + } + + // Print. + if err := printer.Fprint(w, s); err != nil { + return err + } + + return nil +} + +// Save is a convenience for writing a program to a file. +func Save(filename string, p *ir.Program) (err error) { + f, err := os.Create(filename) + if err != nil { + return err + } + defer errutil.CheckClose(&err, f) + return Write(f, p) +} + +// String is a convenience for obtaining a program as an addition chain script +// in string form. +func String(p *ir.Program) (string, error) { + var buf bytes.Buffer + if err := Write(&buf, p); err != nil { + return "", err + } + return buf.String(), nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/ast/ast.go b/vendor/github.com/mmcloughlin/addchain/acc/ast/ast.go new file mode 100644 index 00000000000..28948f55845 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/ast/ast.go @@ -0,0 +1,71 @@ +// Package ast declares abstract syntax tree types for acc programs. +package ast + +// Chain represents a sequence of acc statements for an addition chain +// computation. +type Chain struct { + Statements []Statement +} + +// Statement assigns the result of an expression to a variable. +type Statement struct { + Name Identifier + Expr Expr +} + +// Operator precedence range. +const ( + LowestPrec = 0 + HighestPrec = 4 +) + +// Expr is an expression. +type Expr interface { + Precedence() int +} + +// Operand is an index into an addition chain. +type Operand int + +// Precedence of this expression type. +func (Operand) Precedence() int { return HighestPrec } + +// Identifier is a variable reference. +type Identifier string + +// Precedence of this expression type. +func (Identifier) Precedence() int { return HighestPrec } + +// Add expression. +type Add struct { + X, Y Expr +} + +// Precedence of this expression type. +func (Add) Precedence() int { return 1 } + +// Shift (repeated doubling) expression. +type Shift struct { + X Expr + S uint +} + +// Precedence of this expression type. +func (Shift) Precedence() int { return 2 } + +// Double expression. +type Double struct { + X Expr +} + +// Precedence of this expression type. +func (Double) Precedence() int { return 3 } + +// IsOp reports whether the expression is the result of an operator. +func IsOp(e Expr) bool { + switch e.(type) { + case Add, Shift, Double: + return true + } + return false +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/ast/print.go b/vendor/github.com/mmcloughlin/addchain/acc/ast/print.go new file mode 100644 index 00000000000..4c4fcfea306 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/ast/print.go @@ -0,0 +1,101 @@ +package ast + +import ( + "io" + "os" + + "github.com/mmcloughlin/addchain/internal/errutil" + "github.com/mmcloughlin/addchain/internal/print" +) + +// Print an AST node to standard out. +func Print(n interface{}) error { + return Fprint(os.Stdout, n) +} + +// Fprint writes the AST node n to w. +func Fprint(w io.Writer, n interface{}) error { + p := newprinter(w) + p.node(n) + return p.Error() +} + +type printer struct { + print.Printer +} + +func newprinter(w io.Writer) *printer { + p := &printer{ + Printer: print.New(w), + } + p.SetIndentString(". ") + return p +} + +func (p *printer) node(n interface{}) { + switch n := n.(type) { + case *Chain: + p.enter("chain") + for _, stmt := range n.Statements { + p.statement(stmt) + } + p.leave() + case Statement: + p.statement(n) + case Operand: + p.Linef("operand(%d)", n) + case Identifier: + p.Linef("identifier(%q)", n) + case Add: + p.add(n) + case Double: + p.double(n) + case Shift: + p.shift(n) + default: + p.SetError(errutil.UnexpectedType(n)) + } +} + +func (p *printer) statement(stmt Statement) { + p.enter("statement") + p.Printf("name = ") + p.node(stmt.Name) + p.Printf("expr = ") + p.node(stmt.Expr) + p.leave() +} + +func (p *printer) add(a Add) { + p.enter("add") + p.Printf("x = ") + p.node(a.X) + p.Printf("y = ") + p.node(a.Y) + p.leave() +} + +func (p *printer) double(d Double) { + p.enter("double") + p.Printf("x = ") + p.node(d.X) + p.leave() +} + +func (p *printer) shift(s Shift) { + p.enter("shift") + p.Linef("s = %d", s.S) + p.Printf("x = ") + p.node(s.X) + p.leave() +} + +func (p *printer) enter(name string) { + p.Linef("%s {", name) + p.Indent() +} + +func (p *printer) leave() { + p.Dedent() + p.Linef("}") +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/build.go b/vendor/github.com/mmcloughlin/addchain/acc/build.go new file mode 100644 index 00000000000..38adda34994 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/build.go @@ -0,0 +1,158 @@ +package acc + +import ( + "fmt" + + "github.com/mmcloughlin/addchain/acc/ast" + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/acc/pass" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// complexitylimit is the maximum number of operators the builder will allow an +// expression to have. +const complexitylimit = 5 + +// Build AST from a program in intermediate representation. +func Build(p *ir.Program) (*ast.Chain, error) { + // Run some analysis passes first. + err := pass.Exec(p, + pass.Func(pass.ReadCounts), + pass.NameByteValues, + pass.NameXRuns, + ) + if err != nil { + return nil, err + } + + // Delegate to builder. + b := newbuilder(p) + if err := b.process(); err != nil { + return nil, err + } + + return b.chain, nil +} + +type builder struct { + chain *ast.Chain + prog *ir.Program + expr map[int]ast.Expr +} + +func newbuilder(p *ir.Program) *builder { + return &builder{ + chain: &ast.Chain{}, + prog: p, + expr: map[int]ast.Expr{}, + } +} + +func (b *builder) process() error { + insts := b.prog.Instructions + n := len(insts) + complexity := 0 + for i := 0; i < n; i++ { + complexity++ + inst := insts[i] + out := inst.Output + + // Build expression for the result of this instruction. + e, err := b.operator(inst.Op) + if err != nil { + return err + } + + b.expr[out.Index] = e + + // If this output is read only by the following instruction, we don't need to + // commit it to a variable. + anon := out.Identifier == "" + usedonce := b.prog.ReadCount[out.Index] == 1 + usednext := i+1 < n && ir.HasInput(insts[i+1].Op, out.Index) + if anon && usedonce && usednext && complexity < complexitylimit { + continue + } + + // Otherwise write a statement for it. + b.commit(inst.Output) + complexity = 0 + } + + // Clear the name of the final statement. + b.chain.Statements[len(b.chain.Statements)-1].Name = "" + + return nil +} + +func (b *builder) operator(op ir.Op) (ast.Expr, error) { + switch o := op.(type) { + case ir.Add: + return b.add(o) + case ir.Double: + return ast.Double{ + X: b.operand(o.X), + }, nil + case ir.Shift: + return ast.Shift{ + X: b.operand(o.X), + S: o.S, + }, nil + default: + return nil, errutil.UnexpectedType(op) + } +} + +func (b *builder) add(a ir.Add) (ast.Expr, error) { + // Addition operator construction is slightly delcate, since operand order + // determines ordering of execution. By the design of instruction processing + // above, the only way we can have multi-operator expressions is with a + // sequence of operands that are used only once and in the following + // instruction. This implies that only one of x and y can be an operator + // expression. In order to preserve execution order, whichever one that is + // needs to be the first operand. + + x := b.operand(a.X) + y := b.operand(a.Y) + + switch { + case ast.IsOp(x) && ast.IsOp(y): + return nil, errutil.AssertionFailure("only one of x and y should be an operator expression") + case ast.IsOp(y): + x, y = y, x + case ast.IsOp(x): + // Nothing, it's already the first operand. + } + + return ast.Add{ + X: x, + Y: y, + }, nil +} + +func (b *builder) commit(op *ir.Operand) { + name := ast.Identifier(b.name(op)) + stmt := ast.Statement{ + Name: name, + Expr: b.operand(op), + } + b.chain.Statements = append(b.chain.Statements, stmt) + b.expr[op.Index] = name +} + +// name returns the name for this operand. This is the identifier if available, +// otherwise a sensible default based on the index. +func (b *builder) name(op *ir.Operand) string { + if op.Identifier != "" { + return op.Identifier + } + return fmt.Sprintf("i%d", op.Index) +} + +func (b *builder) operand(op *ir.Operand) ast.Expr { + e, ok := b.expr[op.Index] + if !ok { + return ast.Operand(op.Index) + } + return e +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/decompile.go b/vendor/github.com/mmcloughlin/addchain/acc/decompile.go new file mode 100644 index 00000000000..da05ed932de --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/decompile.go @@ -0,0 +1,58 @@ +package acc + +import ( + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/acc/ir" +) + +// Decompile an unrolled program into concise intermediate representation. +func Decompile(p addchain.Program) (*ir.Program, error) { + numreads := p.ReadCounts() + r := &ir.Program{} + for i := 0; i < len(p); i++ { + op := p[i] + + // Regular addition. + if !op.IsDouble() { + r.AddInstruction(&ir.Instruction{ + Output: ir.Index(i + 1), + Op: ir.Add{ + X: ir.Index(op.I), + Y: ir.Index(op.J), + }, + }) + continue + } + + // We have a double. Look ahead to see if this is a chain of doublings, which + // can be encoded as a shift. Note we can only follow the the doublings as + // long as the intermediate values are not required anywhere else later in the + // program. + j := i + 1 + for ; j < len(p) && numreads[j] == 1 && p[j].I == j && p[j].J == j; j++ { + } + + s := j - i + + // Shift size 1 encoded as a double. + if s == 1 { + r.AddInstruction(&ir.Instruction{ + Output: ir.Index(i + 1), + Op: ir.Double{ + X: ir.Index(op.I), + }, + }) + continue + } + + i = j - 1 + r.AddInstruction(&ir.Instruction{ + Output: ir.Index(i + 1), + Op: ir.Shift{ + X: ir.Index(op.I), + S: uint(s), + }, + }) + } + return r, nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/ir/ir.go b/vendor/github.com/mmcloughlin/addchain/acc/ir/ir.go new file mode 100644 index 00000000000..5f54fec82b2 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/ir/ir.go @@ -0,0 +1,193 @@ +// Package ir declares an intermediate representation for acc programs. +package ir + +import ( + "fmt" + "strings" + + "github.com/mmcloughlin/addchain" +) + +// Program is a sequence of acc instructions. +type Program struct { + Instructions []*Instruction + + // Pass/analysis results. + Operands map[int]*Operand + ReadCount map[int]int + Program addchain.Program + Chain addchain.Chain + Temporaries []string +} + +// AddInstruction appends an instruction to the program. +func (p *Program) AddInstruction(i *Instruction) { + p.Instructions = append(p.Instructions, i) +} + +// Output returns the output of the last instruction. +func (p Program) Output() *Operand { + last := len(p.Instructions) - 1 + return p.Instructions[last].Output +} + +// Clone returns a copy of p. Pass results are not copied and would need to be +// rerun on the clone. +func (p Program) Clone() *Program { + c := &Program{} + for _, inst := range p.Instructions { + c.Instructions = append(c.Instructions, inst.Clone()) + } + return c +} + +func (p Program) String() string { + var b strings.Builder + for _, i := range p.Instructions { + fmt.Fprintln(&b, i) + } + return b.String() +} + +// Operand represents an element of an addition chain, with an optional +// identifier. +type Operand struct { + Identifier string + Index int +} + +// NewOperand builds a named operand for index i. +func NewOperand(name string, i int) *Operand { + return &Operand{ + Identifier: name, + Index: i, + } +} + +// Index builds an unnamed operand for index i. +func Index(i int) *Operand { + return NewOperand("", i) +} + +// One is the first element in the addition chain, which by definition always +// has the value 1. +var One = Index(0) + +// Clone returns a copy of the operand. +func (o Operand) Clone() *Operand { + clone := o + return &clone +} + +func (o Operand) String() string { + if len(o.Identifier) > 0 { + return o.Identifier + } + return fmt.Sprintf("[%d]", o.Index) +} + +// Instruction assigns the result of an operation to an operand. +type Instruction struct { + Output *Operand + Op Op +} + +// Operands returns the input and output operands. +func (i Instruction) Operands() []*Operand { + return append(i.Op.Inputs(), i.Output) +} + +// Clone returns a copy of the instruction. +func (i Instruction) Clone() *Instruction { + return &Instruction{ + Output: i.Output.Clone(), + Op: i.Op.Clone(), + } +} + +func (i Instruction) String() string { + return fmt.Sprintf("%s \u2190 %s", i.Output, i.Op) +} + +// Op is an operation. +type Op interface { + Inputs() []*Operand + Clone() Op + String() string +} + +// Add is an addition operation. +type Add struct { + X, Y *Operand +} + +// Inputs returns the addends. +func (a Add) Inputs() []*Operand { + return []*Operand{a.X, a.Y} +} + +// Clone returns a copy of the operation. +func (a Add) Clone() Op { + return Add{ + X: a.X.Clone(), + Y: a.Y.Clone(), + } +} + +func (a Add) String() string { + return fmt.Sprintf("%s + %s", a.X, a.Y) +} + +// Double is a double operation. +type Double struct { + X *Operand +} + +// Inputs returns the operand. +func (d Double) Inputs() []*Operand { + return []*Operand{d.X} +} + +// Clone returns a copy of the operation. +func (d Double) Clone() Op { + return Double{ + X: d.X.Clone(), + } +} + +func (d Double) String() string { + return fmt.Sprintf("2 * %s", d.X) +} + +// Shift represents a shift-left operation, equivalent to repeat doubling. +type Shift struct { + X *Operand + S uint +} + +// Inputs returns the operand to be shifted. +func (s Shift) Inputs() []*Operand { + return []*Operand{s.X} +} + +// Clone returns a copy of the operation. +func (s Shift) Clone() Op { + return Shift{ + X: s.X.Clone(), + S: s.S, + } +} + +func (s Shift) String() string { + return fmt.Sprintf("%s \u226a %d", s.X, s.S) +} + +// HasInput reports whether the given operation takes idx as an input. +func HasInput(op Op, idx int) bool { + for _, input := range op.Inputs() { + if input.Index == idx { + return true + } + } + return false +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/parse/acc.peg b/vendor/github.com/mmcloughlin/addchain/acc/parse/acc.peg new file mode 100644 index 00000000000..bd80d7e5789 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/parse/acc.peg @@ -0,0 +1,131 @@ +{ + +package parser + +func exprs(first, rest interface{}) []ast.Expr { + es := []ast.Expr{first.(ast.Expr)} + if rest == nil { + return es + } + for _, i := range rest.([]interface{}) { + es = append(es, i.([]interface{})[3].(ast.Expr)) + } + return es +} + +} + +// Chain + +Chain <- as:Assignment* r:Return _ EOF { + ch := &ast.Chain{} + for _, a := range as.([]interface{}) { + ch.Statements = append(ch.Statements, a.(ast.Statement)) + } + ch.Statements = append(ch.Statements, r.(ast.Statement)) + return ch, nil +} + +// Statements + +Assignment <- _ n:Identifier _ '=' _ e:Expr _ EOL { + return ast.Statement{ + Name: n.(ast.Identifier), + Expr: e.(ast.Expr), + }, nil +} + +Return <- _ ("return" __)? e:Expr _ EOL? { + return ast.Statement{ + Name: "", + Expr: e.(ast.Expr), + }, nil +} + +// Expressions + +Expr <- e:AddExpr { + return e, nil +} + +AddExpr <- _ x:ShiftExpr rest:(_ AddOperator _ ShiftExpr)* _ { + es := exprs(x, rest) + r := es[0] + for _, e := range es[1:] { + r = ast.Add{ + X: r, + Y: e, + } + } + return r, nil +} + +ShiftExpr <- _ x:BaseExpr _ ShiftOperator _ s:UintLiteral _ { + return ast.Shift{ + X: x.(ast.Expr), + S: s.(uint), + }, nil +} / _ DoubleOperator _ x:BaseExpr { + return ast.Double{ + X: x.(ast.Expr), + }, nil +} / BaseExpr + +BaseExpr <- ParenExpr / Operand + +ParenExpr <- '(' _ e:Expr _ ')' { + return e, nil +} + +// Operators + +AddOperator <- '+' / "add" + +ShiftOperator <- "<<" / "shl" + +DoubleOperator <- '2' _ '*' / "dbl" + +// Operands + +Operand <- op:( One / Index / Identifier ) { + return op, nil +} + +One <- '1' { + return ast.Operand(0), nil +} + +Index <- '[' _ idx:UintLiteral _ ']' { + return ast.Operand(idx.(uint)), nil +} + +// Identifiers + +Identifier <- [a-zA-Z_] [a-zA-Z0-9_]* { + return ast.Identifier(c.text), nil +} + +// Primitives + +UintLiteral <- u64:Uint64Literal { + return uint(u64.(uint64)), nil +} + +Uint64Literal <- (HexUintLiteral / OctalUintLiteral / DecimalUintLiteral) { + return strconv.ParseUint(string(c.text), 0, 64) +} + +DecimalUintLiteral <- [0-9]+ + +HexUintLiteral <- "0x" [0-9a-fA-F]+ + +OctalUintLiteral <- '0' [0-7]+ + +// Character classes + +__ <- Whitespace+ +_ <- Whitespace* + +Whitespace <- [ \t\r] +EOL <- '\n' +EOF <- !. diff --git a/vendor/github.com/mmcloughlin/addchain/acc/parse/internal/parser/zparser.go b/vendor/github.com/mmcloughlin/addchain/acc/parse/internal/parser/zparser.go new file mode 100644 index 00000000000..88ce3a8f618 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/parse/internal/parser/zparser.go @@ -0,0 +1,2203 @@ +// Code generated by pigeon; DO NOT EDIT. + +package parser + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "math" + "os" + "sort" + "strconv" + "strings" + "sync" + "unicode" + "unicode/utf8" + + "github.com/mmcloughlin/addchain/acc/ast" +) + +func exprs(first, rest interface{}) []ast.Expr { + es := []ast.Expr{first.(ast.Expr)} + if rest == nil { + return es + } + for _, i := range rest.([]interface{}) { + es = append(es, i.([]interface{})[3].(ast.Expr)) + } + return es +} + +var g = &grammar{ + rules: []*rule{ + { + name: "Chain", + pos: position{line: 20, col: 1, offset: 290}, + expr: &actionExpr{ + pos: position{line: 20, col: 10, offset: 299}, + run: (*parser).callonChain1, + expr: &seqExpr{ + pos: position{line: 20, col: 10, offset: 299}, + exprs: []interface{}{ + &labeledExpr{ + pos: position{line: 20, col: 10, offset: 299}, + label: "as", + expr: &zeroOrMoreExpr{ + pos: position{line: 20, col: 13, offset: 302}, + expr: &ruleRefExpr{ + pos: position{line: 20, col: 13, offset: 302}, + name: "Assignment", + }, + }, + }, + &labeledExpr{ + pos: position{line: 20, col: 25, offset: 314}, + label: "r", + expr: &ruleRefExpr{ + pos: position{line: 20, col: 27, offset: 316}, + name: "Return", + }, + }, + &ruleRefExpr{ + pos: position{line: 20, col: 34, offset: 323}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 20, col: 36, offset: 325}, + name: "EOF", + }, + }, + }, + }, + }, + { + name: "Assignment", + pos: position{line: 31, col: 1, offset: 566}, + expr: &actionExpr{ + pos: position{line: 31, col: 15, offset: 580}, + run: (*parser).callonAssignment1, + expr: &seqExpr{ + pos: position{line: 31, col: 15, offset: 580}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 31, col: 15, offset: 580}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 31, col: 17, offset: 582}, + label: "n", + expr: &ruleRefExpr{ + pos: position{line: 31, col: 19, offset: 584}, + name: "Identifier", + }, + }, + &ruleRefExpr{ + pos: position{line: 31, col: 30, offset: 595}, + name: "_", + }, + &litMatcher{ + pos: position{line: 31, col: 32, offset: 597}, + val: "=", + ignoreCase: false, + want: "\"=\"", + }, + &ruleRefExpr{ + pos: position{line: 31, col: 36, offset: 601}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 31, col: 38, offset: 603}, + label: "e", + expr: &ruleRefExpr{ + pos: position{line: 31, col: 40, offset: 605}, + name: "Expr", + }, + }, + &ruleRefExpr{ + pos: position{line: 31, col: 45, offset: 610}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 31, col: 47, offset: 612}, + name: "EOL", + }, + }, + }, + }, + }, + { + name: "Return", + pos: position{line: 38, col: 1, offset: 720}, + expr: &actionExpr{ + pos: position{line: 38, col: 11, offset: 730}, + run: (*parser).callonReturn1, + expr: &seqExpr{ + pos: position{line: 38, col: 11, offset: 730}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 38, col: 11, offset: 730}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 38, col: 13, offset: 732}, + expr: &seqExpr{ + pos: position{line: 38, col: 14, offset: 733}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 38, col: 14, offset: 733}, + val: "return", + ignoreCase: false, + want: "\"return\"", + }, + &ruleRefExpr{ + pos: position{line: 38, col: 23, offset: 742}, + name: "__", + }, + }, + }, + }, + &labeledExpr{ + pos: position{line: 38, col: 28, offset: 747}, + label: "e", + expr: &ruleRefExpr{ + pos: position{line: 38, col: 30, offset: 749}, + name: "Expr", + }, + }, + &ruleRefExpr{ + pos: position{line: 38, col: 35, offset: 754}, + name: "_", + }, + &zeroOrOneExpr{ + pos: position{line: 38, col: 37, offset: 756}, + expr: &ruleRefExpr{ + pos: position{line: 38, col: 37, offset: 756}, + name: "EOL", + }, + }, + }, + }, + }, + }, + { + name: "Expr", + pos: position{line: 47, col: 1, offset: 865}, + expr: &actionExpr{ + pos: position{line: 47, col: 9, offset: 873}, + run: (*parser).callonExpr1, + expr: &labeledExpr{ + pos: position{line: 47, col: 9, offset: 873}, + label: "e", + expr: &ruleRefExpr{ + pos: position{line: 47, col: 11, offset: 875}, + name: "AddExpr", + }, + }, + }, + }, + { + name: "AddExpr", + pos: position{line: 51, col: 1, offset: 906}, + expr: &actionExpr{ + pos: position{line: 51, col: 12, offset: 917}, + run: (*parser).callonAddExpr1, + expr: &seqExpr{ + pos: position{line: 51, col: 12, offset: 917}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 51, col: 12, offset: 917}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 51, col: 14, offset: 919}, + label: "x", + expr: &ruleRefExpr{ + pos: position{line: 51, col: 16, offset: 921}, + name: "ShiftExpr", + }, + }, + &labeledExpr{ + pos: position{line: 51, col: 26, offset: 931}, + label: "rest", + expr: &zeroOrMoreExpr{ + pos: position{line: 51, col: 31, offset: 936}, + expr: &seqExpr{ + pos: position{line: 51, col: 32, offset: 937}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 51, col: 32, offset: 937}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 51, col: 34, offset: 939}, + name: "AddOperator", + }, + &ruleRefExpr{ + pos: position{line: 51, col: 46, offset: 951}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 51, col: 48, offset: 953}, + name: "ShiftExpr", + }, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 51, col: 60, offset: 965}, + name: "_", + }, + }, + }, + }, + }, + { + name: "ShiftExpr", + pos: position{line: 63, col: 1, offset: 1134}, + expr: &choiceExpr{ + pos: position{line: 63, col: 14, offset: 1147}, + alternatives: []interface{}{ + &actionExpr{ + pos: position{line: 63, col: 14, offset: 1147}, + run: (*parser).callonShiftExpr2, + expr: &seqExpr{ + pos: position{line: 63, col: 14, offset: 1147}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 63, col: 14, offset: 1147}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 63, col: 16, offset: 1149}, + label: "x", + expr: &ruleRefExpr{ + pos: position{line: 63, col: 18, offset: 1151}, + name: "BaseExpr", + }, + }, + &ruleRefExpr{ + pos: position{line: 63, col: 27, offset: 1160}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 63, col: 29, offset: 1162}, + name: "ShiftOperator", + }, + &ruleRefExpr{ + pos: position{line: 63, col: 43, offset: 1176}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 63, col: 45, offset: 1178}, + label: "s", + expr: &ruleRefExpr{ + pos: position{line: 63, col: 47, offset: 1180}, + name: "UintLiteral", + }, + }, + &ruleRefExpr{ + pos: position{line: 63, col: 59, offset: 1192}, + name: "_", + }, + }, + }, + }, + &actionExpr{ + pos: position{line: 68, col: 5, offset: 1279}, + run: (*parser).callonShiftExpr13, + expr: &seqExpr{ + pos: position{line: 68, col: 5, offset: 1279}, + exprs: []interface{}{ + &ruleRefExpr{ + pos: position{line: 68, col: 5, offset: 1279}, + name: "_", + }, + &ruleRefExpr{ + pos: position{line: 68, col: 7, offset: 1281}, + name: "DoubleOperator", + }, + &ruleRefExpr{ + pos: position{line: 68, col: 22, offset: 1296}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 68, col: 24, offset: 1298}, + label: "x", + expr: &ruleRefExpr{ + pos: position{line: 68, col: 26, offset: 1300}, + name: "BaseExpr", + }, + }, + }, + }, + }, + &ruleRefExpr{ + pos: position{line: 72, col: 5, offset: 1374}, + name: "BaseExpr", + }, + }, + }, + }, + { + name: "BaseExpr", + pos: position{line: 74, col: 1, offset: 1384}, + expr: &choiceExpr{ + pos: position{line: 74, col: 13, offset: 1396}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 74, col: 13, offset: 1396}, + name: "ParenExpr", + }, + &ruleRefExpr{ + pos: position{line: 74, col: 25, offset: 1408}, + name: "Operand", + }, + }, + }, + }, + { + name: "ParenExpr", + pos: position{line: 76, col: 1, offset: 1417}, + expr: &actionExpr{ + pos: position{line: 76, col: 14, offset: 1430}, + run: (*parser).callonParenExpr1, + expr: &seqExpr{ + pos: position{line: 76, col: 14, offset: 1430}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 76, col: 14, offset: 1430}, + val: "(", + ignoreCase: false, + want: "\"(\"", + }, + &ruleRefExpr{ + pos: position{line: 76, col: 18, offset: 1434}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 76, col: 20, offset: 1436}, + label: "e", + expr: &ruleRefExpr{ + pos: position{line: 76, col: 22, offset: 1438}, + name: "Expr", + }, + }, + &ruleRefExpr{ + pos: position{line: 76, col: 27, offset: 1443}, + name: "_", + }, + &litMatcher{ + pos: position{line: 76, col: 29, offset: 1445}, + val: ")", + ignoreCase: false, + want: "\")\"", + }, + }, + }, + }, + }, + { + name: "AddOperator", + pos: position{line: 82, col: 1, offset: 1486}, + expr: &choiceExpr{ + pos: position{line: 82, col: 16, offset: 1501}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 82, col: 16, offset: 1501}, + val: "+", + ignoreCase: false, + want: "\"+\"", + }, + &litMatcher{ + pos: position{line: 82, col: 22, offset: 1507}, + val: "add", + ignoreCase: false, + want: "\"add\"", + }, + }, + }, + }, + { + name: "ShiftOperator", + pos: position{line: 84, col: 1, offset: 1514}, + expr: &choiceExpr{ + pos: position{line: 84, col: 18, offset: 1531}, + alternatives: []interface{}{ + &litMatcher{ + pos: position{line: 84, col: 18, offset: 1531}, + val: "<<", + ignoreCase: false, + want: "\"<<\"", + }, + &litMatcher{ + pos: position{line: 84, col: 25, offset: 1538}, + val: "shl", + ignoreCase: false, + want: "\"shl\"", + }, + }, + }, + }, + { + name: "DoubleOperator", + pos: position{line: 86, col: 1, offset: 1545}, + expr: &choiceExpr{ + pos: position{line: 86, col: 19, offset: 1563}, + alternatives: []interface{}{ + &seqExpr{ + pos: position{line: 86, col: 19, offset: 1563}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 86, col: 19, offset: 1563}, + val: "2", + ignoreCase: false, + want: "\"2\"", + }, + &ruleRefExpr{ + pos: position{line: 86, col: 23, offset: 1567}, + name: "_", + }, + &litMatcher{ + pos: position{line: 86, col: 25, offset: 1569}, + val: "*", + ignoreCase: false, + want: "\"*\"", + }, + }, + }, + &litMatcher{ + pos: position{line: 86, col: 31, offset: 1575}, + val: "dbl", + ignoreCase: false, + want: "\"dbl\"", + }, + }, + }, + }, + { + name: "Operand", + pos: position{line: 90, col: 1, offset: 1595}, + expr: &actionExpr{ + pos: position{line: 90, col: 12, offset: 1606}, + run: (*parser).callonOperand1, + expr: &labeledExpr{ + pos: position{line: 90, col: 12, offset: 1606}, + label: "op", + expr: &choiceExpr{ + pos: position{line: 90, col: 17, offset: 1611}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 90, col: 17, offset: 1611}, + name: "One", + }, + &ruleRefExpr{ + pos: position{line: 90, col: 23, offset: 1617}, + name: "Index", + }, + &ruleRefExpr{ + pos: position{line: 90, col: 31, offset: 1625}, + name: "Identifier", + }, + }, + }, + }, + }, + }, + { + name: "One", + pos: position{line: 94, col: 1, offset: 1662}, + expr: &actionExpr{ + pos: position{line: 94, col: 8, offset: 1669}, + run: (*parser).callonOne1, + expr: &litMatcher{ + pos: position{line: 94, col: 8, offset: 1669}, + val: "1", + ignoreCase: false, + want: "\"1\"", + }, + }, + }, + { + name: "Index", + pos: position{line: 98, col: 1, offset: 1709}, + expr: &actionExpr{ + pos: position{line: 98, col: 10, offset: 1718}, + run: (*parser).callonIndex1, + expr: &seqExpr{ + pos: position{line: 98, col: 10, offset: 1718}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 98, col: 10, offset: 1718}, + val: "[", + ignoreCase: false, + want: "\"[\"", + }, + &ruleRefExpr{ + pos: position{line: 98, col: 14, offset: 1722}, + name: "_", + }, + &labeledExpr{ + pos: position{line: 98, col: 16, offset: 1724}, + label: "idx", + expr: &ruleRefExpr{ + pos: position{line: 98, col: 20, offset: 1728}, + name: "UintLiteral", + }, + }, + &ruleRefExpr{ + pos: position{line: 98, col: 32, offset: 1740}, + name: "_", + }, + &litMatcher{ + pos: position{line: 98, col: 34, offset: 1742}, + val: "]", + ignoreCase: false, + want: "\"]\"", + }, + }, + }, + }, + }, + { + name: "Identifier", + pos: position{line: 104, col: 1, offset: 1807}, + expr: &actionExpr{ + pos: position{line: 104, col: 15, offset: 1821}, + run: (*parser).callonIdentifier1, + expr: &seqExpr{ + pos: position{line: 104, col: 15, offset: 1821}, + exprs: []interface{}{ + &charClassMatcher{ + pos: position{line: 104, col: 15, offset: 1821}, + val: "[a-zA-Z_]", + chars: []rune{'_'}, + ranges: []rune{'a', 'z', 'A', 'Z'}, + ignoreCase: false, + inverted: false, + }, + &zeroOrMoreExpr{ + pos: position{line: 104, col: 25, offset: 1831}, + expr: &charClassMatcher{ + pos: position{line: 104, col: 25, offset: 1831}, + val: "[a-zA-Z0-9_]", + chars: []rune{'_'}, + ranges: []rune{'a', 'z', 'A', 'Z', '0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + }, + { + name: "UintLiteral", + pos: position{line: 110, col: 1, offset: 1904}, + expr: &actionExpr{ + pos: position{line: 110, col: 16, offset: 1919}, + run: (*parser).callonUintLiteral1, + expr: &labeledExpr{ + pos: position{line: 110, col: 16, offset: 1919}, + label: "u64", + expr: &ruleRefExpr{ + pos: position{line: 110, col: 20, offset: 1923}, + name: "Uint64Literal", + }, + }, + }, + }, + { + name: "Uint64Literal", + pos: position{line: 114, col: 1, offset: 1977}, + expr: &actionExpr{ + pos: position{line: 114, col: 18, offset: 1994}, + run: (*parser).callonUint64Literal1, + expr: &choiceExpr{ + pos: position{line: 114, col: 19, offset: 1995}, + alternatives: []interface{}{ + &ruleRefExpr{ + pos: position{line: 114, col: 19, offset: 1995}, + name: "HexUintLiteral", + }, + &ruleRefExpr{ + pos: position{line: 114, col: 36, offset: 2012}, + name: "OctalUintLiteral", + }, + &ruleRefExpr{ + pos: position{line: 114, col: 55, offset: 2031}, + name: "DecimalUintLiteral", + }, + }, + }, + }, + }, + { + name: "DecimalUintLiteral", + pos: position{line: 118, col: 1, offset: 2108}, + expr: &oneOrMoreExpr{ + pos: position{line: 118, col: 23, offset: 2130}, + expr: &charClassMatcher{ + pos: position{line: 118, col: 23, offset: 2130}, + val: "[0-9]", + ranges: []rune{'0', '9'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + { + name: "HexUintLiteral", + pos: position{line: 120, col: 1, offset: 2138}, + expr: &seqExpr{ + pos: position{line: 120, col: 19, offset: 2156}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 120, col: 19, offset: 2156}, + val: "0x", + ignoreCase: false, + want: "\"0x\"", + }, + &oneOrMoreExpr{ + pos: position{line: 120, col: 24, offset: 2161}, + expr: &charClassMatcher{ + pos: position{line: 120, col: 24, offset: 2161}, + val: "[0-9a-fA-F]", + ranges: []rune{'0', '9', 'a', 'f', 'A', 'F'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + { + name: "OctalUintLiteral", + pos: position{line: 122, col: 1, offset: 2175}, + expr: &seqExpr{ + pos: position{line: 122, col: 21, offset: 2195}, + exprs: []interface{}{ + &litMatcher{ + pos: position{line: 122, col: 21, offset: 2195}, + val: "0", + ignoreCase: false, + want: "\"0\"", + }, + &oneOrMoreExpr{ + pos: position{line: 122, col: 25, offset: 2199}, + expr: &charClassMatcher{ + pos: position{line: 122, col: 25, offset: 2199}, + val: "[0-7]", + ranges: []rune{'0', '7'}, + ignoreCase: false, + inverted: false, + }, + }, + }, + }, + }, + { + name: "__", + pos: position{line: 126, col: 1, offset: 2229}, + expr: &oneOrMoreExpr{ + pos: position{line: 126, col: 7, offset: 2235}, + expr: &ruleRefExpr{ + pos: position{line: 126, col: 7, offset: 2235}, + name: "Whitespace", + }, + }, + }, + { + name: "_", + pos: position{line: 127, col: 1, offset: 2247}, + expr: &zeroOrMoreExpr{ + pos: position{line: 127, col: 6, offset: 2252}, + expr: &ruleRefExpr{ + pos: position{line: 127, col: 6, offset: 2252}, + name: "Whitespace", + }, + }, + }, + { + name: "Whitespace", + pos: position{line: 129, col: 1, offset: 2265}, + expr: &charClassMatcher{ + pos: position{line: 129, col: 15, offset: 2279}, + val: "[ \\t\\r]", + chars: []rune{' ', '\t', '\r'}, + ignoreCase: false, + inverted: false, + }, + }, + { + name: "EOL", + pos: position{line: 130, col: 1, offset: 2287}, + expr: &litMatcher{ + pos: position{line: 130, col: 8, offset: 2294}, + val: "\n", + ignoreCase: false, + want: "\"\\n\"", + }, + }, + { + name: "EOF", + pos: position{line: 131, col: 1, offset: 2299}, + expr: ¬Expr{ + pos: position{line: 131, col: 8, offset: 2306}, + expr: &anyMatcher{ + line: 131, col: 9, offset: 2307, + }, + }, + }, + }, +} + +func (c *current) onChain1(as, r interface{}) (interface{}, error) { + ch := &ast.Chain{} + for _, a := range as.([]interface{}) { + ch.Statements = append(ch.Statements, a.(ast.Statement)) + } + ch.Statements = append(ch.Statements, r.(ast.Statement)) + return ch, nil +} + +func (p *parser) callonChain1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onChain1(stack["as"], stack["r"]) +} + +func (c *current) onAssignment1(n, e interface{}) (interface{}, error) { + return ast.Statement{ + Name: n.(ast.Identifier), + Expr: e.(ast.Expr), + }, nil +} + +func (p *parser) callonAssignment1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onAssignment1(stack["n"], stack["e"]) +} + +func (c *current) onReturn1(e interface{}) (interface{}, error) { + return ast.Statement{ + Name: "", + Expr: e.(ast.Expr), + }, nil +} + +func (p *parser) callonReturn1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onReturn1(stack["e"]) +} + +func (c *current) onExpr1(e interface{}) (interface{}, error) { + return e, nil +} + +func (p *parser) callonExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onExpr1(stack["e"]) +} + +func (c *current) onAddExpr1(x, rest interface{}) (interface{}, error) { + es := exprs(x, rest) + r := es[0] + for _, e := range es[1:] { + r = ast.Add{ + X: r, + Y: e, + } + } + return r, nil +} + +func (p *parser) callonAddExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onAddExpr1(stack["x"], stack["rest"]) +} + +func (c *current) onShiftExpr2(x, s interface{}) (interface{}, error) { + return ast.Shift{ + X: x.(ast.Expr), + S: s.(uint), + }, nil +} + +func (p *parser) callonShiftExpr2() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onShiftExpr2(stack["x"], stack["s"]) +} + +func (c *current) onShiftExpr13(x interface{}) (interface{}, error) { + return ast.Double{ + X: x.(ast.Expr), + }, nil +} + +func (p *parser) callonShiftExpr13() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onShiftExpr13(stack["x"]) +} + +func (c *current) onParenExpr1(e interface{}) (interface{}, error) { + return e, nil +} + +func (p *parser) callonParenExpr1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onParenExpr1(stack["e"]) +} + +func (c *current) onOperand1(op interface{}) (interface{}, error) { + return op, nil +} + +func (p *parser) callonOperand1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onOperand1(stack["op"]) +} + +func (c *current) onOne1() (interface{}, error) { + return ast.Operand(0), nil +} + +func (p *parser) callonOne1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onOne1() +} + +func (c *current) onIndex1(idx interface{}) (interface{}, error) { + return ast.Operand(idx.(uint)), nil +} + +func (p *parser) callonIndex1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onIndex1(stack["idx"]) +} + +func (c *current) onIdentifier1() (interface{}, error) { + return ast.Identifier(c.text), nil +} + +func (p *parser) callonIdentifier1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onIdentifier1() +} + +func (c *current) onUintLiteral1(u64 interface{}) (interface{}, error) { + return uint(u64.(uint64)), nil +} + +func (p *parser) callonUintLiteral1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onUintLiteral1(stack["u64"]) +} + +func (c *current) onUint64Literal1() (interface{}, error) { + return strconv.ParseUint(string(c.text), 0, 64) +} + +func (p *parser) callonUint64Literal1() (interface{}, error) { + stack := p.vstack[len(p.vstack)-1] + _ = stack + return p.cur.onUint64Literal1() +} + +var ( + // errNoRule is returned when the grammar to parse has no rule. + errNoRule = errors.New("grammar has no rule") + + // errInvalidEntrypoint is returned when the specified entrypoint rule + // does not exit. + errInvalidEntrypoint = errors.New("invalid entrypoint") + + // errInvalidEncoding is returned when the source is not properly + // utf8-encoded. + errInvalidEncoding = errors.New("invalid encoding") + + // errMaxExprCnt is used to signal that the maximum number of + // expressions have been parsed. + errMaxExprCnt = errors.New("max number of expresssions parsed") +) + +// Option is a function that can set an option on the parser. It returns +// the previous setting as an Option. +type Option func(*parser) Option + +// MaxExpressions creates an Option to stop parsing after the provided +// number of expressions have been parsed, if the value is 0 then the parser will +// parse for as many steps as needed (possibly an infinite number). +// +// The default for maxExprCnt is 0. +func MaxExpressions(maxExprCnt uint64) Option { + return func(p *parser) Option { + oldMaxExprCnt := p.maxExprCnt + p.maxExprCnt = maxExprCnt + return MaxExpressions(oldMaxExprCnt) + } +} + +// Entrypoint creates an Option to set the rule name to use as entrypoint. +// The rule name must have been specified in the -alternate-entrypoints +// if generating the parser with the -optimize-grammar flag, otherwise +// it may have been optimized out. Passing an empty string sets the +// entrypoint to the first rule in the grammar. +// +// The default is to start parsing at the first rule in the grammar. +func Entrypoint(ruleName string) Option { + return func(p *parser) Option { + oldEntrypoint := p.entrypoint + p.entrypoint = ruleName + if ruleName == "" { + p.entrypoint = g.rules[0].name + } + return Entrypoint(oldEntrypoint) + } +} + +// Statistics adds a user provided Stats struct to the parser to allow +// the user to process the results after the parsing has finished. +// Also the key for the "no match" counter is set. +// +// Example usage: +// +// input := "input" +// stats := Stats{} +// _, err := Parse("input-file", []byte(input), Statistics(&stats, "no match")) +// if err != nil { +// log.Panicln(err) +// } +// b, err := json.MarshalIndent(stats.ChoiceAltCnt, "", " ") +// if err != nil { +// log.Panicln(err) +// } +// fmt.Println(string(b)) +// +func Statistics(stats *Stats, choiceNoMatch string) Option { + return func(p *parser) Option { + oldStats := p.Stats + p.Stats = stats + oldChoiceNoMatch := p.choiceNoMatch + p.choiceNoMatch = choiceNoMatch + if p.Stats.ChoiceAltCnt == nil { + p.Stats.ChoiceAltCnt = make(map[string]map[string]int) + } + return Statistics(oldStats, oldChoiceNoMatch) + } +} + +// Debug creates an Option to set the debug flag to b. When set to true, +// debugging information is printed to stdout while parsing. +// +// The default is false. +func Debug(b bool) Option { + return func(p *parser) Option { + old := p.debug + p.debug = b + return Debug(old) + } +} + +// Memoize creates an Option to set the memoize flag to b. When set to true, +// the parser will cache all results so each expression is evaluated only +// once. This guarantees linear parsing time even for pathological cases, +// at the expense of more memory and slower times for typical cases. +// +// The default is false. +func Memoize(b bool) Option { + return func(p *parser) Option { + old := p.memoize + p.memoize = b + return Memoize(old) + } +} + +// AllowInvalidUTF8 creates an Option to allow invalid UTF-8 bytes. +// Every invalid UTF-8 byte is treated as a utf8.RuneError (U+FFFD) +// by character class matchers and is matched by the any matcher. +// The returned matched value, c.text and c.offset are NOT affected. +// +// The default is false. +func AllowInvalidUTF8(b bool) Option { + return func(p *parser) Option { + old := p.allowInvalidUTF8 + p.allowInvalidUTF8 = b + return AllowInvalidUTF8(old) + } +} + +// Recover creates an Option to set the recover flag to b. When set to +// true, this causes the parser to recover from panics and convert it +// to an error. Setting it to false can be useful while debugging to +// access the full stack trace. +// +// The default is true. +func Recover(b bool) Option { + return func(p *parser) Option { + old := p.recover + p.recover = b + return Recover(old) + } +} + +// GlobalStore creates an Option to set a key to a certain value in +// the globalStore. +func GlobalStore(key string, value interface{}) Option { + return func(p *parser) Option { + old := p.cur.globalStore[key] + p.cur.globalStore[key] = value + return GlobalStore(key, old) + } +} + +// InitState creates an Option to set a key to a certain value in +// the global "state" store. +func InitState(key string, value interface{}) Option { + return func(p *parser) Option { + old := p.cur.state[key] + p.cur.state[key] = value + return InitState(key, old) + } +} + +// ParseFile parses the file identified by filename. +func ParseFile(filename string, opts ...Option) (i interface{}, err error) { + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer func() { + if closeErr := f.Close(); closeErr != nil { + err = closeErr + } + }() + return ParseReader(filename, f, opts...) +} + +// ParseReader parses the data from r using filename as information in the +// error messages. +func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, error) { + b, err := ioutil.ReadAll(r) + if err != nil { + return nil, err + } + + return Parse(filename, b, opts...) +} + +// Parse parses the data from b using filename as information in the +// error messages. +func Parse(filename string, b []byte, opts ...Option) (interface{}, error) { + return newParser(filename, b, opts...).parse(g) +} + +// position records a position in the text. +type position struct { + line, col, offset int +} + +func (p position) String() string { + return strconv.Itoa(p.line) + ":" + strconv.Itoa(p.col) + " [" + strconv.Itoa(p.offset) + "]" +} + +// savepoint stores all state required to go back to this point in the +// parser. +type savepoint struct { + position + rn rune + w int +} + +type current struct { + pos position // start position of the match + text []byte // raw text of the match + + // state is a store for arbitrary key,value pairs that the user wants to be + // tied to the backtracking of the parser. + // This is always rolled back if a parsing rule fails. + state storeDict + + // globalStore is a general store for the user to store arbitrary key-value + // pairs that they need to manage and that they do not want tied to the + // backtracking of the parser. This is only modified by the user and never + // rolled back by the parser. It is always up to the user to keep this in a + // consistent state. + globalStore storeDict +} + +type storeDict map[string]interface{} + +// the AST types... + +type grammar struct { + pos position + rules []*rule +} + +type rule struct { + pos position + name string + displayName string + expr interface{} +} + +type choiceExpr struct { + pos position + alternatives []interface{} +} + +type actionExpr struct { + pos position + expr interface{} + run func(*parser) (interface{}, error) +} + +type recoveryExpr struct { + pos position + expr interface{} + recoverExpr interface{} + failureLabel []string +} + +type seqExpr struct { + pos position + exprs []interface{} +} + +type throwExpr struct { + pos position + label string +} + +type labeledExpr struct { + pos position + label string + expr interface{} +} + +type expr struct { + pos position + expr interface{} +} + +type andExpr expr +type notExpr expr +type zeroOrOneExpr expr +type zeroOrMoreExpr expr +type oneOrMoreExpr expr + +type ruleRefExpr struct { + pos position + name string +} + +type stateCodeExpr struct { + pos position + run func(*parser) error +} + +type andCodeExpr struct { + pos position + run func(*parser) (bool, error) +} + +type notCodeExpr struct { + pos position + run func(*parser) (bool, error) +} + +type litMatcher struct { + pos position + val string + ignoreCase bool + want string +} + +type charClassMatcher struct { + pos position + val string + basicLatinChars [128]bool + chars []rune + ranges []rune + classes []*unicode.RangeTable + ignoreCase bool + inverted bool +} + +type anyMatcher position + +// errList cumulates the errors found by the parser. +type errList []error + +func (e *errList) add(err error) { + *e = append(*e, err) +} + +func (e errList) err() error { + if len(e) == 0 { + return nil + } + e.dedupe() + return e +} + +func (e *errList) dedupe() { + var cleaned []error + set := make(map[string]bool) + for _, err := range *e { + if msg := err.Error(); !set[msg] { + set[msg] = true + cleaned = append(cleaned, err) + } + } + *e = cleaned +} + +func (e errList) Error() string { + switch len(e) { + case 0: + return "" + case 1: + return e[0].Error() + default: + var buf bytes.Buffer + + for i, err := range e { + if i > 0 { + buf.WriteRune('\n') + } + buf.WriteString(err.Error()) + } + return buf.String() + } +} + +// parserError wraps an error with a prefix indicating the rule in which +// the error occurred. The original error is stored in the Inner field. +type parserError struct { + Inner error + pos position + prefix string + expected []string +} + +// Error returns the error message. +func (p *parserError) Error() string { + return p.prefix + ": " + p.Inner.Error() +} + +// newParser creates a parser with the specified input source and options. +func newParser(filename string, b []byte, opts ...Option) *parser { + stats := Stats{ + ChoiceAltCnt: make(map[string]map[string]int), + } + + p := &parser{ + filename: filename, + errs: new(errList), + data: b, + pt: savepoint{position: position{line: 1}}, + recover: true, + cur: current{ + state: make(storeDict), + globalStore: make(storeDict), + }, + maxFailPos: position{col: 1, line: 1}, + maxFailExpected: make([]string, 0, 20), + Stats: &stats, + // start rule is rule [0] unless an alternate entrypoint is specified + entrypoint: g.rules[0].name, + } + p.setOptions(opts) + + if p.maxExprCnt == 0 { + p.maxExprCnt = math.MaxUint64 + } + + return p +} + +// setOptions applies the options to the parser. +func (p *parser) setOptions(opts []Option) { + for _, opt := range opts { + opt(p) + } +} + +type resultTuple struct { + v interface{} + b bool + end savepoint +} + +const choiceNoMatch = -1 + +// Stats stores some statistics, gathered during parsing +type Stats struct { + // ExprCnt counts the number of expressions processed during parsing + // This value is compared to the maximum number of expressions allowed + // (set by the MaxExpressions option). + ExprCnt uint64 + + // ChoiceAltCnt is used to count for each ordered choice expression, + // which alternative is used how may times. + // These numbers allow to optimize the order of the ordered choice expression + // to increase the performance of the parser + // + // The outer key of ChoiceAltCnt is composed of the name of the rule as well + // as the line and the column of the ordered choice. + // The inner key of ChoiceAltCnt is the number (one-based) of the matching alternative. + // For each alternative the number of matches are counted. If an ordered choice does not + // match, a special counter is incremented. The name of this counter is set with + // the parser option Statistics. + // For an alternative to be included in ChoiceAltCnt, it has to match at least once. + ChoiceAltCnt map[string]map[string]int +} + +type parser struct { + filename string + pt savepoint + cur current + + data []byte + errs *errList + + depth int + recover bool + debug bool + + memoize bool + // memoization table for the packrat algorithm: + // map[offset in source] map[expression or rule] {value, match} + memo map[int]map[interface{}]resultTuple + + // rules table, maps the rule identifier to the rule node + rules map[string]*rule + // variables stack, map of label to value + vstack []map[string]interface{} + // rule stack, allows identification of the current rule in errors + rstack []*rule + + // parse fail + maxFailPos position + maxFailExpected []string + maxFailInvertExpected bool + + // max number of expressions to be parsed + maxExprCnt uint64 + // entrypoint for the parser + entrypoint string + + allowInvalidUTF8 bool + + *Stats + + choiceNoMatch string + // recovery expression stack, keeps track of the currently available recovery expression, these are traversed in reverse + recoveryStack []map[string]interface{} +} + +// push a variable set on the vstack. +func (p *parser) pushV() { + if cap(p.vstack) == len(p.vstack) { + // create new empty slot in the stack + p.vstack = append(p.vstack, nil) + } else { + // slice to 1 more + p.vstack = p.vstack[:len(p.vstack)+1] + } + + // get the last args set + m := p.vstack[len(p.vstack)-1] + if m != nil && len(m) == 0 { + // empty map, all good + return + } + + m = make(map[string]interface{}) + p.vstack[len(p.vstack)-1] = m +} + +// pop a variable set from the vstack. +func (p *parser) popV() { + // if the map is not empty, clear it + m := p.vstack[len(p.vstack)-1] + if len(m) > 0 { + // GC that map + p.vstack[len(p.vstack)-1] = nil + } + p.vstack = p.vstack[:len(p.vstack)-1] +} + +// push a recovery expression with its labels to the recoveryStack +func (p *parser) pushRecovery(labels []string, expr interface{}) { + if cap(p.recoveryStack) == len(p.recoveryStack) { + // create new empty slot in the stack + p.recoveryStack = append(p.recoveryStack, nil) + } else { + // slice to 1 more + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)+1] + } + + m := make(map[string]interface{}, len(labels)) + for _, fl := range labels { + m[fl] = expr + } + p.recoveryStack[len(p.recoveryStack)-1] = m +} + +// pop a recovery expression from the recoveryStack +func (p *parser) popRecovery() { + // GC that map + p.recoveryStack[len(p.recoveryStack)-1] = nil + + p.recoveryStack = p.recoveryStack[:len(p.recoveryStack)-1] +} + +func (p *parser) print(prefix, s string) string { + if !p.debug { + return s + } + + fmt.Printf("%s %d:%d:%d: %s [%#U]\n", + prefix, p.pt.line, p.pt.col, p.pt.offset, s, p.pt.rn) + return s +} + +func (p *parser) in(s string) string { + p.depth++ + return p.print(strings.Repeat(" ", p.depth)+">", s) +} + +func (p *parser) out(s string) string { + p.depth-- + return p.print(strings.Repeat(" ", p.depth)+"<", s) +} + +func (p *parser) addErr(err error) { + p.addErrAt(err, p.pt.position, []string{}) +} + +func (p *parser) addErrAt(err error, pos position, expected []string) { + var buf bytes.Buffer + if p.filename != "" { + buf.WriteString(p.filename) + } + if buf.Len() > 0 { + buf.WriteString(":") + } + buf.WriteString(fmt.Sprintf("%d:%d (%d)", pos.line, pos.col, pos.offset)) + if len(p.rstack) > 0 { + if buf.Len() > 0 { + buf.WriteString(": ") + } + rule := p.rstack[len(p.rstack)-1] + if rule.displayName != "" { + buf.WriteString("rule " + rule.displayName) + } else { + buf.WriteString("rule " + rule.name) + } + } + pe := &parserError{Inner: err, pos: pos, prefix: buf.String(), expected: expected} + p.errs.add(pe) +} + +func (p *parser) failAt(fail bool, pos position, want string) { + // process fail if parsing fails and not inverted or parsing succeeds and invert is set + if fail == p.maxFailInvertExpected { + if pos.offset < p.maxFailPos.offset { + return + } + + if pos.offset > p.maxFailPos.offset { + p.maxFailPos = pos + p.maxFailExpected = p.maxFailExpected[:0] + } + + if p.maxFailInvertExpected { + want = "!" + want + } + p.maxFailExpected = append(p.maxFailExpected, want) + } +} + +// read advances the parser to the next rune. +func (p *parser) read() { + p.pt.offset += p.pt.w + rn, n := utf8.DecodeRune(p.data[p.pt.offset:]) + p.pt.rn = rn + p.pt.w = n + p.pt.col++ + if rn == '\n' { + p.pt.line++ + p.pt.col = 0 + } + + if rn == utf8.RuneError && n == 1 { // see utf8.DecodeRune + if !p.allowInvalidUTF8 { + p.addErr(errInvalidEncoding) + } + } +} + +// restore parser position to the savepoint pt. +func (p *parser) restore(pt savepoint) { + if p.debug { + defer p.out(p.in("restore")) + } + if pt.offset == p.pt.offset { + return + } + p.pt = pt +} + +// Cloner is implemented by any value that has a Clone method, which returns a +// copy of the value. This is mainly used for types which are not passed by +// value (e.g map, slice, chan) or structs that contain such types. +// +// This is used in conjunction with the global state feature to create proper +// copies of the state to allow the parser to properly restore the state in +// the case of backtracking. +type Cloner interface { + Clone() interface{} +} + +var statePool = &sync.Pool{ + New: func() interface{} { return make(storeDict) }, +} + +func (sd storeDict) Discard() { + for k := range sd { + delete(sd, k) + } + statePool.Put(sd) +} + +// clone and return parser current state. +func (p *parser) cloneState() storeDict { + if p.debug { + defer p.out(p.in("cloneState")) + } + + state := statePool.Get().(storeDict) + for k, v := range p.cur.state { + if c, ok := v.(Cloner); ok { + state[k] = c.Clone() + } else { + state[k] = v + } + } + return state +} + +// restore parser current state to the state storeDict. +// every restoreState should applied only one time for every cloned state +func (p *parser) restoreState(state storeDict) { + if p.debug { + defer p.out(p.in("restoreState")) + } + p.cur.state.Discard() + p.cur.state = state +} + +// get the slice of bytes from the savepoint start to the current position. +func (p *parser) sliceFrom(start savepoint) []byte { + return p.data[start.position.offset:p.pt.position.offset] +} + +func (p *parser) getMemoized(node interface{}) (resultTuple, bool) { + if len(p.memo) == 0 { + return resultTuple{}, false + } + m := p.memo[p.pt.offset] + if len(m) == 0 { + return resultTuple{}, false + } + res, ok := m[node] + return res, ok +} + +func (p *parser) setMemoized(pt savepoint, node interface{}, tuple resultTuple) { + if p.memo == nil { + p.memo = make(map[int]map[interface{}]resultTuple) + } + m := p.memo[pt.offset] + if m == nil { + m = make(map[interface{}]resultTuple) + p.memo[pt.offset] = m + } + m[node] = tuple +} + +func (p *parser) buildRulesTable(g *grammar) { + p.rules = make(map[string]*rule, len(g.rules)) + for _, r := range g.rules { + p.rules[r.name] = r + } +} + +func (p *parser) parse(g *grammar) (val interface{}, err error) { + if len(g.rules) == 0 { + p.addErr(errNoRule) + return nil, p.errs.err() + } + + // TODO : not super critical but this could be generated + p.buildRulesTable(g) + + if p.recover { + // panic can be used in action code to stop parsing immediately + // and return the panic as an error. + defer func() { + if e := recover(); e != nil { + if p.debug { + defer p.out(p.in("panic handler")) + } + val = nil + switch e := e.(type) { + case error: + p.addErr(e) + default: + p.addErr(fmt.Errorf("%v", e)) + } + err = p.errs.err() + } + }() + } + + startRule, ok := p.rules[p.entrypoint] + if !ok { + p.addErr(errInvalidEntrypoint) + return nil, p.errs.err() + } + + p.read() // advance to first rune + val, ok = p.parseRule(startRule) + if !ok { + if len(*p.errs) == 0 { + // If parsing fails, but no errors have been recorded, the expected values + // for the farthest parser position are returned as error. + maxFailExpectedMap := make(map[string]struct{}, len(p.maxFailExpected)) + for _, v := range p.maxFailExpected { + maxFailExpectedMap[v] = struct{}{} + } + expected := make([]string, 0, len(maxFailExpectedMap)) + eof := false + if _, ok := maxFailExpectedMap["!."]; ok { + delete(maxFailExpectedMap, "!.") + eof = true + } + for k := range maxFailExpectedMap { + expected = append(expected, k) + } + sort.Strings(expected) + if eof { + expected = append(expected, "EOF") + } + p.addErrAt(errors.New("no match found, expected: "+listJoin(expected, ", ", "or")), p.maxFailPos, expected) + } + + return nil, p.errs.err() + } + return val, p.errs.err() +} + +func listJoin(list []string, sep string, lastSep string) string { + switch len(list) { + case 0: + return "" + case 1: + return list[0] + default: + return strings.Join(list[:len(list)-1], sep) + " " + lastSep + " " + list[len(list)-1] + } +} + +func (p *parser) parseRule(rule *rule) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRule " + rule.name)) + } + + if p.memoize { + res, ok := p.getMemoized(rule) + if ok { + p.restore(res.end) + return res.v, res.b + } + } + + start := p.pt + p.rstack = append(p.rstack, rule) + p.pushV() + val, ok := p.parseExpr(rule.expr) + p.popV() + p.rstack = p.rstack[:len(p.rstack)-1] + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } + + if p.memoize { + p.setMemoized(start, rule, resultTuple{val, ok, p.pt}) + } + return val, ok +} + +func (p *parser) parseExpr(expr interface{}) (interface{}, bool) { + var pt savepoint + + if p.memoize { + res, ok := p.getMemoized(expr) + if ok { + p.restore(res.end) + return res.v, res.b + } + pt = p.pt + } + + p.ExprCnt++ + if p.ExprCnt > p.maxExprCnt { + panic(errMaxExprCnt) + } + + var val interface{} + var ok bool + switch expr := expr.(type) { + case *actionExpr: + val, ok = p.parseActionExpr(expr) + case *andCodeExpr: + val, ok = p.parseAndCodeExpr(expr) + case *andExpr: + val, ok = p.parseAndExpr(expr) + case *anyMatcher: + val, ok = p.parseAnyMatcher(expr) + case *charClassMatcher: + val, ok = p.parseCharClassMatcher(expr) + case *choiceExpr: + val, ok = p.parseChoiceExpr(expr) + case *labeledExpr: + val, ok = p.parseLabeledExpr(expr) + case *litMatcher: + val, ok = p.parseLitMatcher(expr) + case *notCodeExpr: + val, ok = p.parseNotCodeExpr(expr) + case *notExpr: + val, ok = p.parseNotExpr(expr) + case *oneOrMoreExpr: + val, ok = p.parseOneOrMoreExpr(expr) + case *recoveryExpr: + val, ok = p.parseRecoveryExpr(expr) + case *ruleRefExpr: + val, ok = p.parseRuleRefExpr(expr) + case *seqExpr: + val, ok = p.parseSeqExpr(expr) + case *stateCodeExpr: + val, ok = p.parseStateCodeExpr(expr) + case *throwExpr: + val, ok = p.parseThrowExpr(expr) + case *zeroOrMoreExpr: + val, ok = p.parseZeroOrMoreExpr(expr) + case *zeroOrOneExpr: + val, ok = p.parseZeroOrOneExpr(expr) + default: + panic(fmt.Sprintf("unknown expression type %T", expr)) + } + if p.memoize { + p.setMemoized(pt, expr, resultTuple{val, ok, p.pt}) + } + return val, ok +} + +func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseActionExpr")) + } + + start := p.pt + val, ok := p.parseExpr(act.expr) + if ok { + p.cur.pos = start.position + p.cur.text = p.sliceFrom(start) + state := p.cloneState() + actVal, err := act.run(p) + if err != nil { + p.addErrAt(err, start.position, []string{}) + } + p.restoreState(state) + + val = actVal + } + if ok && p.debug { + p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) + } + return val, ok +} + +func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAndCodeExpr")) + } + + state := p.cloneState() + + ok, err := and.run(p) + if err != nil { + p.addErr(err) + } + p.restoreState(state) + + return nil, ok +} + +func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAndExpr")) + } + + pt := p.pt + state := p.cloneState() + p.pushV() + _, ok := p.parseExpr(and.expr) + p.popV() + p.restoreState(state) + p.restore(pt) + + return nil, ok +} + +func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseAnyMatcher")) + } + + if p.pt.rn == utf8.RuneError && p.pt.w == 0 { + // EOF - see utf8.DecodeRune + p.failAt(false, p.pt.position, ".") + return nil, false + } + start := p.pt + p.read() + p.failAt(true, start.position, ".") + return p.sliceFrom(start), true +} + +func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseCharClassMatcher")) + } + + cur := p.pt.rn + start := p.pt + + // can't match EOF + if cur == utf8.RuneError && p.pt.w == 0 { // see utf8.DecodeRune + p.failAt(false, start.position, chr.val) + return nil, false + } + + if chr.ignoreCase { + cur = unicode.ToLower(cur) + } + + // try to match in the list of available chars + for _, rn := range chr.chars { + if rn == cur { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of ranges + for i := 0; i < len(chr.ranges); i += 2 { + if cur >= chr.ranges[i] && cur <= chr.ranges[i+1] { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + // try to match in the list of Unicode classes + for _, cl := range chr.classes { + if unicode.Is(cl, cur) { + if chr.inverted { + p.failAt(false, start.position, chr.val) + return nil, false + } + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + } + + if chr.inverted { + p.read() + p.failAt(true, start.position, chr.val) + return p.sliceFrom(start), true + } + p.failAt(false, start.position, chr.val) + return nil, false +} + +func (p *parser) incChoiceAltCnt(ch *choiceExpr, altI int) { + choiceIdent := fmt.Sprintf("%s %d:%d", p.rstack[len(p.rstack)-1].name, ch.pos.line, ch.pos.col) + m := p.ChoiceAltCnt[choiceIdent] + if m == nil { + m = make(map[string]int) + p.ChoiceAltCnt[choiceIdent] = m + } + // We increment altI by 1, so the keys do not start at 0 + alt := strconv.Itoa(altI + 1) + if altI == choiceNoMatch { + alt = p.choiceNoMatch + } + m[alt]++ +} + +func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseChoiceExpr")) + } + + for altI, alt := range ch.alternatives { + // dummy assignment to prevent compile error if optimized + _ = altI + + state := p.cloneState() + + p.pushV() + val, ok := p.parseExpr(alt) + p.popV() + if ok { + p.incChoiceAltCnt(ch, altI) + return val, ok + } + p.restoreState(state) + } + p.incChoiceAltCnt(ch, choiceNoMatch) + return nil, false +} + +func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseLabeledExpr")) + } + + p.pushV() + val, ok := p.parseExpr(lab.expr) + p.popV() + if ok && lab.label != "" { + m := p.vstack[len(p.vstack)-1] + m[lab.label] = val + } + return val, ok +} + +func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseLitMatcher")) + } + + start := p.pt + for _, want := range lit.val { + cur := p.pt.rn + if lit.ignoreCase { + cur = unicode.ToLower(cur) + } + if cur != want { + p.failAt(false, start.position, lit.want) + p.restore(start) + return nil, false + } + p.read() + } + p.failAt(true, start.position, lit.want) + return p.sliceFrom(start), true +} + +func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseNotCodeExpr")) + } + + state := p.cloneState() + + ok, err := not.run(p) + if err != nil { + p.addErr(err) + } + p.restoreState(state) + + return nil, !ok +} + +func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseNotExpr")) + } + + pt := p.pt + state := p.cloneState() + p.pushV() + p.maxFailInvertExpected = !p.maxFailInvertExpected + _, ok := p.parseExpr(not.expr) + p.maxFailInvertExpected = !p.maxFailInvertExpected + p.popV() + p.restoreState(state) + p.restore(pt) + + return nil, !ok +} + +func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseOneOrMoreExpr")) + } + + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + if len(vals) == 0 { + // did not match once, no match + return nil, false + } + return vals, true + } + vals = append(vals, val) + } +} + +func (p *parser) parseRecoveryExpr(recover *recoveryExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRecoveryExpr (" + strings.Join(recover.failureLabel, ",") + ")")) + } + + p.pushRecovery(recover.failureLabel, recover.recoverExpr) + val, ok := p.parseExpr(recover.expr) + p.popRecovery() + + return val, ok +} + +func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseRuleRefExpr " + ref.name)) + } + + if ref.name == "" { + panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) + } + + rule := p.rules[ref.name] + if rule == nil { + p.addErr(fmt.Errorf("undefined rule: %s", ref.name)) + return nil, false + } + return p.parseRule(rule) +} + +func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseSeqExpr")) + } + + vals := make([]interface{}, 0, len(seq.exprs)) + + pt := p.pt + state := p.cloneState() + for _, expr := range seq.exprs { + val, ok := p.parseExpr(expr) + if !ok { + p.restoreState(state) + p.restore(pt) + return nil, false + } + vals = append(vals, val) + } + return vals, true +} + +func (p *parser) parseStateCodeExpr(state *stateCodeExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseStateCodeExpr")) + } + + err := state.run(p) + if err != nil { + p.addErr(err) + } + return nil, true +} + +func (p *parser) parseThrowExpr(expr *throwExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseThrowExpr")) + } + + for i := len(p.recoveryStack) - 1; i >= 0; i-- { + if recoverExpr, ok := p.recoveryStack[i][expr.label]; ok { + if val, ok := p.parseExpr(recoverExpr); ok { + return val, ok + } + } + } + + return nil, false +} + +func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrMoreExpr")) + } + + var vals []interface{} + + for { + p.pushV() + val, ok := p.parseExpr(expr.expr) + p.popV() + if !ok { + return vals, true + } + vals = append(vals, val) + } +} + +func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) { + if p.debug { + defer p.out(p.in("parseZeroOrOneExpr")) + } + + p.pushV() + val, _ := p.parseExpr(expr.expr) + p.popV() + // whether it matched or not, consider it a match + return val, true +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/parse/parse.go b/vendor/github.com/mmcloughlin/addchain/acc/parse/parse.go new file mode 100644 index 00000000000..8523e146970 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/parse/parse.go @@ -0,0 +1,35 @@ +// Package parse implements a parser for acc programs. +package parse + +import ( + "io" + "strings" + + "github.com/mmcloughlin/addchain/acc/ast" + "github.com/mmcloughlin/addchain/acc/parse/internal/parser" +) + +//go:generate pigeon -o internal/parser/zparser.go acc.peg + +// File parses filename. +func File(filename string) (*ast.Chain, error) { + return cast(parser.ParseFile(filename)) +} + +// Reader parses the data from r using filename as information in +// error messages. +func Reader(filename string, r io.Reader) (*ast.Chain, error) { + return cast(parser.ParseReader(filename, r)) +} + +// String parses s. +func String(s string) (*ast.Chain, error) { + return Reader("string", strings.NewReader(s)) +} + +func cast(i interface{}, err error) (*ast.Chain, error) { + if err != nil { + return nil, err + } + return i.(*ast.Chain), nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/pass/alloc.go b/vendor/github.com/mmcloughlin/addchain/acc/pass/alloc.go new file mode 100644 index 00000000000..5c8e97d8232 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/pass/alloc.go @@ -0,0 +1,98 @@ +package pass + +import ( + "fmt" + + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/internal/container/heap" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// Allocator pass assigns a minimal number of temporary variables to execute a program. +type Allocator struct { + // Input is the name of the input variable. Note this is index 0, or the + // identity element of the addition chain. + Input string + + // Output is the name to give to the final output of the addition chain. This + // variable may itself be used as a temporary during execution. + Output string + + // Format defines how to format any temporary variables. This format string + // must accept one integer value. For example "t%d" would be a reasonable + // choice. + Format string +} + +// Execute performs temporary variable allocation. +func (a Allocator) Execute(p *ir.Program) error { + // Canonicalize operands and delete all names. + if err := Exec(p, Func(CanonicalizeOperands), Func(ClearNames)); err != nil { + return err + } + + // Initialize allocation. This maps operand index to variable index. The + // inidicies 0 and 1 are special, reserved for the input and output + // respectively. Any indicies above that are temporaries. + out := p.Output() + allocation := map[int]int{ + 0: 0, + out.Index: 1, + } + n := 2 + + // Keep a heap of available indicies. Initially none. + available := heap.NewMinInts() + + // Process instructions in reverse. + for i := len(p.Instructions) - 1; i >= 0; i-- { + inst := p.Instructions[i] + + // The output operand variable now becomes available. + v, ok := allocation[inst.Output.Index] + if !ok { + return errutil.AssertionFailure("output operand %d missing allocation", inst.Output.Index) + } + available.Push(v) + + // Inputs may need variables, if they are not already live. + for _, input := range inst.Op.Inputs() { + _, ok := allocation[input.Index] + if ok { + continue + } + + // If there's nothing available, we'll need one more temporary. + if available.Empty() { + available.Push(n) + n++ + } + + allocation[input.Index] = available.Pop() + } + } + + // Record allocation. + for _, op := range p.Operands { + op.Identifier = a.name(allocation[op.Index]) + } + + temps := []string{} + for i := 2; i < n; i++ { + temps = append(temps, a.name(i)) + } + p.Temporaries = temps + + return nil +} + +func (a Allocator) name(v int) string { + switch v { + case 0: + return a.Input + case 1: + return a.Output + default: + return fmt.Sprintf(a.Format, v-2) + } +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/pass/eval.go b/vendor/github.com/mmcloughlin/addchain/acc/pass/eval.go new file mode 100644 index 00000000000..2dd85b1dd73 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/pass/eval.go @@ -0,0 +1,57 @@ +package pass + +import ( + "errors" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// Compile generates the fully unrolled sequence of additions. The result is +// stored in the Program field. +func Compile(p *ir.Program) error { + if p.Program != nil { + return nil + } + + p.Program = addchain.Program{} + for _, i := range p.Instructions { + var out int + var err error + + switch op := i.Op.(type) { + case ir.Add: + out, err = p.Program.Add(op.X.Index, op.Y.Index) + case ir.Double: + out, err = p.Program.Double(op.X.Index) + case ir.Shift: + out, err = p.Program.Shift(op.X.Index, op.S) + default: + return errutil.UnexpectedType(op) + } + + if err != nil { + return err + } + if out != i.Output.Index { + return errors.New("incorrect output index") + } + } + + return nil +} + +// Eval evaluates the program and places the result in the Chain field. +func Eval(p *ir.Program) error { + if p.Chain != nil { + return nil + } + + if err := Compile(p); err != nil { + return err + } + + p.Chain = p.Program.Evaluate() + return nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/pass/naming.go b/vendor/github.com/mmcloughlin/addchain/acc/pass/naming.go new file mode 100644 index 00000000000..e417ef1cf45 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/pass/naming.go @@ -0,0 +1,89 @@ +package pass + +import ( + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// References: +// +// [curvechains] Brian Smith. The Most Efficient Known Addition Chains for Field Element and +// Scalar Inversion for the Most Popular and Most Unpopular Elliptic Curves. 2017. +// https://briansmith.org/ecc-inversion-addition-chains-01 (accessed June 30, 2019) + +// Naming conventions described in [curvechains]. +var ( + NameByteValues = NameBinaryValues(8, "_%b") + NameXRuns = NameBinaryRuns("x%d") +) + +// ClearNames deletes all operand names. +func ClearNames(p *ir.Program) error { + if err := CanonicalizeOperands(p); err != nil { + return err + } + + for _, operand := range p.Operands { + operand.Identifier = "" + } + + return nil +} + +// NameBinaryValues assigns variable names to operands with values less than 2ᵏ. +// The identifier is determined from the format string, which should expect to +// take one *big.Int argument. +func NameBinaryValues(k int, format string) Interface { + return NameOperands(func(_ int, x *big.Int) string { + if x.BitLen() > k { + return "" + } + return fmt.Sprintf(format, x) + }) +} + +// NameBinaryRuns assigns variable names to operands with values of the form 2ⁿ +// - 1. The identifier is determined from the format string, which takes the +// length of the run as a parameter. +func NameBinaryRuns(format string) Interface { + return NameOperands(func(_ int, x *big.Int) string { + n := uint(x.BitLen()) + if !bigint.Equal(x, bigint.Ones(n)) { + return "" + } + return fmt.Sprintf(format, n) + }) +} + +// NameOperands builds a pass that names operands according to the given scheme. +func NameOperands(name func(int, *big.Int) string) Interface { + return Func(func(p *ir.Program) error { + // We need canonical operands, and we need to know the chain values. + if err := Exec(p, Func(CanonicalizeOperands), Func(Eval)); err != nil { + return err + } + + for _, operand := range p.Operands { + // Skip if it already has a name. + if operand.Identifier != "" { + continue + } + + // Fetch referenced value. + idx := operand.Index + if idx >= len(p.Chain) { + return errutil.AssertionFailure("operand index %d out of bounds", idx) + } + x := p.Chain[idx] + + // Set name. + operand.Identifier = name(idx, x) + } + + return nil + }) +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/pass/pass.go b/vendor/github.com/mmcloughlin/addchain/acc/pass/pass.go new file mode 100644 index 00000000000..a9f1ee23d09 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/pass/pass.go @@ -0,0 +1,117 @@ +// Package pass implements analysis and processing passes on acc programs. +package pass + +import ( + "fmt" + + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// Interface for a processing pass. +type Interface interface { + Execute(*ir.Program) error +} + +// Func adapts a function to the pass Interface. +type Func func(*ir.Program) error + +// Execute calls p. +func (f Func) Execute(p *ir.Program) error { + return f(p) +} + +// Concat returns a pass that executes the given passes in order, stopping on +// the first error. +func Concat(passes ...Interface) Interface { + return Func(func(p *ir.Program) error { + for _, pass := range passes { + if err := pass.Execute(p); err != nil { + return err + } + } + return nil + }) +} + +// Exec is a convenience for executing a list of passes on p. +func Exec(p *ir.Program, passes ...Interface) error { + return Concat(passes...).Execute(p) +} + +// CanonicalizeOperands ensures there is only one Operand object for each +// operand index in the program. In particular, this ensures there are not +// conflicting names for the same index. Populates the Operands field of the +// program. +func CanonicalizeOperands(p *ir.Program) error { + if p.Operands != nil { + return nil + } + + p.Operands = map[int]*ir.Operand{} + + // First pass through determines canonical operand for each index. + for _, i := range p.Instructions { + for _, operand := range i.Operands() { + // Look for an existing operand object for this index. + existing, found := p.Operands[operand.Index] + if !found { + p.Operands[operand.Index] = operand + continue + } + + if existing == operand { + continue + } + + // They're different objects. Check for a name conflict. + if existing.Identifier != "" && operand.Identifier != "" && existing.Identifier != operand.Identifier { + return fmt.Errorf("identifier conflict: index %d named %q and %q", operand.Index, operand.Identifier, existing.Identifier) + } + + if operand.Identifier != "" { + existing.Identifier = operand.Identifier + } + } + } + + // Second pass through replaces all operands with the canonical version. + for _, i := range p.Instructions { + switch op := i.Op.(type) { + case ir.Add: + i.Op = ir.Add{ + X: p.Operands[op.X.Index], + Y: p.Operands[op.Y.Index], + } + case ir.Double: + i.Op = ir.Double{ + X: p.Operands[op.X.Index], + } + case ir.Shift: + i.Op = ir.Shift{ + X: p.Operands[op.X.Index], + S: op.S, + } + default: + return errutil.UnexpectedType(op) + } + } + + return nil +} + +// ReadCounts computes how many times each index is read in the program. This +// populates the ReadCount field of the program. +func ReadCounts(p *ir.Program) error { + if p.ReadCount != nil { + return nil + } + + p.ReadCount = map[int]int{} + for _, i := range p.Instructions { + for _, input := range i.Op.Inputs() { + p.ReadCount[input.Index]++ + } + } + return nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/pass/validation.go b/vendor/github.com/mmcloughlin/addchain/acc/pass/validation.go new file mode 100644 index 00000000000..e93ba63c34d --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/pass/validation.go @@ -0,0 +1,28 @@ +package pass + +import ( + "fmt" + + "github.com/mmcloughlin/addchain/acc/ir" +) + +// Validate is a pass to sanity check an intermediate representation program. +var Validate = Func(CheckDanglingInputs) + +// CheckDanglingInputs looks for program inputs that have no instruction +// outputting them. Note this can happen and still be technically correct. For +// example a shift instruction produces many intermediate results, and one of +// these can later be referenced. The resulting program is still correct, but +// undesirable. +func CheckDanglingInputs(p *ir.Program) error { + outputset := map[int]bool{0: true} + for _, i := range p.Instructions { + for _, input := range i.Op.Inputs() { + if !outputset[input.Index] { + return fmt.Errorf("no output instruction for input index %d", input.Index) + } + } + outputset[i.Output.Index] = true + } + return nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/printer/printer.go b/vendor/github.com/mmcloughlin/addchain/acc/printer/printer.go new file mode 100644 index 00000000000..96e8baa9f02 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/printer/printer.go @@ -0,0 +1,131 @@ +// Package printer implements printing of acc AST nodes. +package printer + +import ( + "bytes" + "io" + "os" + + "github.com/mmcloughlin/addchain/acc/ast" + "github.com/mmcloughlin/addchain/internal/errutil" + "github.com/mmcloughlin/addchain/internal/print" +) + +// String prints the AST and returns resulting string. +func String(n interface{}) (string, error) { + b, err := Bytes(n) + if err != nil { + return "", err + } + return string(b), nil +} + +// Bytes prints the AST and returns resulting bytes. +func Bytes(n interface{}) ([]byte, error) { + var buf bytes.Buffer + if err := Fprint(&buf, n); err != nil { + return nil, err + } + return buf.Bytes(), nil +} + +// Print an AST node to standard out. +func Print(n interface{}) error { + return Fprint(os.Stdout, n) +} + +// Fprint writes the AST node n to w. +func Fprint(w io.Writer, n interface{}) error { + p := newprinter(w) + p.node(n) + p.Flush() + return p.Error() +} + +type printer struct { + *print.TabWriter +} + +func newprinter(w io.Writer) *printer { + return &printer{ + TabWriter: print.NewTabWriter(w, 1, 4, 1, ' ', 0), + } +} + +func (p *printer) node(n interface{}) { + switch n := n.(type) { + case *ast.Chain: + for _, stmt := range n.Statements { + p.statement(stmt) + } + case ast.Statement: + p.statement(n) + case ast.Expr: + p.expr(n, nil) + default: + p.SetError(errutil.UnexpectedType(n)) + } +} + +func (p *printer) statement(stmt ast.Statement) { + if len(stmt.Name) > 0 { + p.Printf("%s\t=\t", stmt.Name) + } else { + p.Printf("return\t\t") + } + p.expr(stmt.Expr, nil) + p.NL() +} + +func (p *printer) expr(e, parent ast.Expr) { + // Parens required if the precence of this operator is less than its parent. + if parent != nil && e.Precedence() < parent.Precedence() { + p.Printf("(") + p.expr(e, nil) + p.Printf(")") + return + } + + switch e := e.(type) { + case ast.Operand: + p.operand(e) + case ast.Identifier: + p.identifier(e) + case ast.Add: + p.add(e) + case ast.Double: + p.double(e) + case ast.Shift: + p.shift(e) + default: + p.SetError(errutil.UnexpectedType(e)) + } +} + +func (p *printer) add(a ast.Add) { + p.expr(a.X, a) + p.Printf(" + ") + p.expr(a.Y, a) +} + +func (p *printer) double(d ast.Double) { + p.Printf("2*") + p.expr(d.X, d) +} + +func (p *printer) shift(s ast.Shift) { + p.expr(s.X, s) + p.Printf(" << %d", s.S) +} + +func (p *printer) identifier(name ast.Identifier) { + p.Printf("%s", name) +} + +func (p *printer) operand(op ast.Operand) { + if op == 0 { + p.Printf("1") + } else { + p.Printf("[%d]", op) + } +} diff --git a/vendor/github.com/mmcloughlin/addchain/acc/translate.go b/vendor/github.com/mmcloughlin/addchain/acc/translate.go new file mode 100644 index 00000000000..91bfcf17e46 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/acc/translate.go @@ -0,0 +1,139 @@ +package acc + +import ( + "fmt" + + "github.com/mmcloughlin/addchain/acc/ast" + "github.com/mmcloughlin/addchain/acc/ir" + "github.com/mmcloughlin/addchain/internal/errutil" +) + +// Translate converts an abstract syntax tree to an intermediate representation. +func Translate(c *ast.Chain) (*ir.Program, error) { + s := newstate() + for _, stmt := range c.Statements { + if err := s.statement(stmt); err != nil { + return nil, err + } + } + return s.prog, nil +} + +type state struct { + prog *ir.Program + n int + variable map[ast.Identifier]*ir.Operand +} + +func newstate() *state { + return &state{ + prog: &ir.Program{}, + n: 1, + variable: map[ast.Identifier]*ir.Operand{}, + } +} + +func (s *state) statement(stmt ast.Statement) error { + out, err := s.expr(stmt.Expr) + if err != nil { + return err + } + if err := s.define(stmt.Name, out); err != nil { + return err + } + return nil +} + +func (s *state) expr(expr ast.Expr) (*ir.Operand, error) { + switch e := expr.(type) { + case ast.Operand: + return &ir.Operand{Index: int(e)}, nil + case ast.Identifier: + return s.lookup(e) + case ast.Add: + return s.add(e) + case ast.Double: + return s.double(e) + case ast.Shift: + return s.shift(e) + default: + return nil, errutil.UnexpectedType(e) + } +} + +func (s *state) add(a ast.Add) (*ir.Operand, error) { + x, err := s.expr(a.X) + if err != nil { + return nil, err + } + + y, err := s.expr(a.Y) + if err != nil { + return nil, err + } + + if x.Index > y.Index { + x, y = y, x + } + + out := ir.Index(s.n) + inst := &ir.Instruction{ + Output: out, + Op: ir.Add{X: x, Y: y}, + } + s.prog.AddInstruction(inst) + s.n++ + + return out, nil +} + +func (s *state) double(d ast.Double) (*ir.Operand, error) { + x, err := s.expr(d.X) + if err != nil { + return nil, err + } + + out := ir.Index(s.n) + inst := &ir.Instruction{ + Output: out, + Op: ir.Double{X: x}, + } + s.prog.AddInstruction(inst) + s.n++ + + return out, nil +} + +func (s *state) shift(sh ast.Shift) (*ir.Operand, error) { + x, err := s.expr(sh.X) + if err != nil { + return nil, err + } + + s.n += int(sh.S) + out := ir.Index(s.n - 1) + inst := &ir.Instruction{ + Output: out, + Op: ir.Shift{X: x, S: sh.S}, + } + s.prog.AddInstruction(inst) + + return out, nil +} + +func (s *state) define(name ast.Identifier, op *ir.Operand) error { + if _, found := s.variable[name]; found { + return fmt.Errorf("cannot redefine %q", name) + } + op.Identifier = string(name) + s.variable[name] = op + return nil +} + +func (s state) lookup(name ast.Identifier) (*ir.Operand, error) { + operand, ok := s.variable[name] + if !ok { + return nil, fmt.Errorf("variable %q undefined", name) + } + return operand, nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/alg.go b/vendor/github.com/mmcloughlin/addchain/alg/alg.go new file mode 100644 index 00000000000..0555dacb0d3 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/alg.go @@ -0,0 +1,43 @@ +// Package alg provides base types for addition chain and addition sequence search algorithms. +package alg + +import ( + "math/big" + + "github.com/mmcloughlin/addchain" +) + +// ChainAlgorithm is a method of generating an addition chain for a target integer. +type ChainAlgorithm interface { + // FindChain generates an addition chain ending at target. + FindChain(target *big.Int) (addchain.Chain, error) + + // String returns a name for the algorithm. + String() string +} + +// SequenceAlgorithm is a method of generating an addition sequence for a set of +// target values. +type SequenceAlgorithm interface { + // FindSequence generates an addition chain containing every element of targets. + FindSequence(targets []*big.Int) (addchain.Chain, error) + + // String returns a name for the algorithm. + String() string +} + +// AsChainAlgorithm adapts a sequence algorithm to a chain algorithm. The +// resulting algorithm calls the sequence algorithm with a singleton list +// containing the target. +func AsChainAlgorithm(s SequenceAlgorithm) ChainAlgorithm { + return asChainAlgorithm{s} +} + +type asChainAlgorithm struct { + SequenceAlgorithm +} + +// FindChain calls FindSequence with a singleton list containing the target. +func (a asChainAlgorithm) FindChain(target *big.Int) (addchain.Chain, error) { + return a.FindSequence([]*big.Int{target}) +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/contfrac/contfrac.go b/vendor/github.com/mmcloughlin/addchain/alg/contfrac/contfrac.go new file mode 100644 index 00000000000..3c31a538356 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/contfrac/contfrac.go @@ -0,0 +1,262 @@ +// Package contfrac implements addition sequence algorithms based on continued-fraction expansions. +package contfrac + +import ( + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/bigints" +) + +// References: +// +// [contfrac] F Bergeron, J Berstel, S Brlek and C Duboc. Addition chains using continued +// fractions. Journal of Algorithms. 1989. +// http://www-igm.univ-mlv.fr/~berstel/Articles/1989AdditionChainDuboc.pdf +// [efficientcompaddchain] Bergeron, F., Berstel, J. and Brlek, S. Efficient computation of addition +// chains. Journal de theorie des nombres de Bordeaux. 1994. +// http://www.numdam.org/item/JTNB_1994__6_1_21_0 +// [gencontfrac] Amadou Tall and Ali Yassin Sanghare. Efficient computation of +// addition-subtraction chains using generalized continued Fractions. Cryptology +// ePrint Archive, Report 2013/466. 2013. https://eprint.iacr.org/2013/466 +// [hehcc:exp] Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve +// Cryptography, chapter 9. 2006. +// http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + +// Strategy is a method of choosing the auxiliary integer k in the continued +// fraction method outlined in [efficientcompaddchain]. +type Strategy interface { + // K returns values of k to try given n. + K(n *big.Int) []*big.Int + + // Singleton returns whether every call to K will return one value of k. This + // determines whether the resulting continued fractions sequence algorithm will + // be logarithmic, and therefore suitable for large inputs. + Singleton() bool + + // String returns a name for the strategy. + String() string +} + +// Strategies lists all available continued fraction strategies. +var Strategies = []Strategy{ + BinaryStrategy{}, + CoBinaryStrategy{}, + DichotomicStrategy{}, + SqrtStrategy{}, + TotalStrategy{}, + DyadicStrategy{}, + FermatStrategy{}, +} + +// Algorithm uses the continued fractions method for finding an addition chain +// [contfrac] [efficientcompaddchain]. +type Algorithm struct { + strategy Strategy +} + +// NewAlgorithm builds a continued fractions addition sequence algorithm using +// the provided strategy for selecting the auziallary integer k. +func NewAlgorithm(s Strategy) Algorithm { + return Algorithm{ + strategy: s, + } +} + +func (a Algorithm) String() string { + return fmt.Sprintf("continued_fractions(%s)", a.strategy) +} + +// FindSequence applies the continued fractions method to build a chain +// containing targets. +func (a Algorithm) FindSequence(targets []*big.Int) (addchain.Chain, error) { + bigints.Sort(targets) + return a.chain(targets), nil +} + +func (a Algorithm) minchain(n *big.Int) addchain.Chain { + if bigint.IsPow2(n) { + return bigint.Pow2UpTo(n) + } + + if bigint.EqualInt64(n, 3) { + return bigints.Int64s(1, 2, 3) + } + + var min addchain.Chain + for _, k := range a.strategy.K(n) { + c := a.chain([]*big.Int{k, n}) + if min == nil || len(c) < len(min) { + min = c + } + } + + return min +} + +// chain produces a continued fraction chain for the given values. The slice ns +// must be in ascending order. +func (a Algorithm) chain(ns []*big.Int) addchain.Chain { + k := len(ns) + if k == 1 || ns[k-2].Cmp(bigint.One()) <= 0 { + return a.minchain(ns[k-1]) + } + + q, r := new(big.Int), new(big.Int) + q.DivMod(ns[k-1], ns[k-2], r) + + cq := a.minchain(q) + remaining := bigints.Clone(ns[:k-1]) + + if bigint.IsZero(r) { + return addchain.Product(a.chain(remaining), cq) + } + + remaining = bigints.InsertSortedUnique(remaining, r) + return addchain.Plus(addchain.Product(a.chain(remaining), cq), r) +} + +// BinaryStrategy implements the binary strategy, which just sets k = floor(n/2). See [efficientcompaddchain] page 26. +// Since this is a singleton strategy it gives rise to a logarithmic sequence algoirithm that may not be optimal. +type BinaryStrategy struct{} + +func (BinaryStrategy) String() string { return "binary" } + +// Singleton returns true, since the binary strategy returns a single proposal +// for k. +func (BinaryStrategy) Singleton() bool { return true } + +// K returns floor(n/2). +func (BinaryStrategy) K(n *big.Int) []*big.Int { + k := new(big.Int).Rsh(n, 1) + return []*big.Int{k} +} + +// CoBinaryStrategy implements the co-binary strategy, also referred to as the +// "modified-binary" strategy. See [efficientcompaddchain] page 26 or +// [gencontfrac] page 6. Since this is a singleton strategy it gives rise to a +// logarithmic sequence algorithm that may not be optimal. +type CoBinaryStrategy struct{} + +func (CoBinaryStrategy) String() string { return "co_binary" } + +// Singleton returns true, since the co-binary strategy returns a single +// proposal for k. +func (CoBinaryStrategy) Singleton() bool { return true } + +// K returns floor(n/2) when n is even, or floor((n+1)/2) when n is odd. +func (CoBinaryStrategy) K(n *big.Int) []*big.Int { + k := bigint.Clone(n) + if k.Bit(0) == 1 { + k.Add(k, bigint.One()) + } + k.Rsh(k, 1) + return []*big.Int{k} +} + +// TotalStrategy returns all possible values of k less than n. This will result +// in the optimal continued fraction chain at a complexity of O(n² log²(n)). +// Note that the optimal continued fraction chain is not necessarily the optimal +// chain. Must not be used for large inputs. +type TotalStrategy struct{} + +func (TotalStrategy) String() string { return "total" } + +// Singleton returns false, since the total strategy returns more than once k. +func (TotalStrategy) Singleton() bool { return false } + +// K returns {2,, 3, ..., n-1}. +func (TotalStrategy) K(n *big.Int) []*big.Int { + ks := []*big.Int{} + k := big.NewInt(2) + one := bigint.One() + for k.Cmp(n) < 0 { + ks = append(ks, bigint.Clone(k)) + k.Add(k, one) + } + return ks +} + +// DyadicStrategy implements the Dyadic Strategy, defined in +// [efficientcompaddchain] page 28. This gives rise to a sequence algorithm with +// complexity O(n*log³(n)). Must not be used for large inputs. +type DyadicStrategy struct{} + +func (DyadicStrategy) String() string { return "dyadic" } + +// Singleton returns false, since the dyadic strategy returns more than once k. +func (DyadicStrategy) Singleton() bool { return false } + +// K returns floor( n / 2ʲ ) for all j. +func (DyadicStrategy) K(n *big.Int) []*big.Int { + ks := []*big.Int{} + k := new(big.Int).Rsh(n, 1) + one := bigint.One() + for k.Cmp(one) > 0 { + ks = append(ks, bigint.Clone(k)) + k.Rsh(k, 1) + } + return ks +} + +// FermatStrategy implements Fermat's Strategy, defined in +// [efficientcompaddchain] page 28. This returns a set of possible k of size +// O(log(log(n))), giving rise to a faster algorithm than the Dyadic strategy. +// This has been shown to be near optimal for small inputs. Must not be used for +// large inputs. +type FermatStrategy struct{} + +func (FermatStrategy) String() string { return "fermat" } + +// Singleton returns false, since Fermat's strategy returns more than once k. +func (FermatStrategy) Singleton() bool { return false } + +// K returns floor( n / 2^(2^j) ) for all j. +func (FermatStrategy) K(n *big.Int) []*big.Int { + ks := []*big.Int{} + k := new(big.Int).Rsh(n, 1) + one := bigint.One() + s := uint(1) + for k.Cmp(one) > 0 { + ks = append(ks, bigint.Clone(k)) + k.Rsh(k, s) + s *= 2 + } + return ks +} + +// DichotomicStrategy is a singleton strategy, defined in +// [efficientcompaddchain] page 28. This gives rise to a logarithmic sequence +// algorithm, but the result is not necessarily optimal. +type DichotomicStrategy struct{} + +func (DichotomicStrategy) String() string { return "dichotomic" } + +// Singleton returns true, since the dichotomic strategy suggests just one k. +func (DichotomicStrategy) Singleton() bool { return true } + +// K returns only one suggestion for k, namely floor( n / 2ʰ ) where h = log2(n)/2. +func (DichotomicStrategy) K(n *big.Int) []*big.Int { + l := n.BitLen() + h := uint(l) / 2 + k := new(big.Int).Div(n, bigint.Pow2(h)) + return []*big.Int{k} +} + +// SqrtStrategy chooses k to be floor(sqrt(n)). See [gencontfrac] page 6. Since +// this is a singleton strategy, it gives rise to a logarithmic sequence +// algorithm that's not necessarily optimal. +type SqrtStrategy struct{} + +func (SqrtStrategy) String() string { return "sqrt" } + +// Singleton returns true, since the square root strategy suggests just one k. +func (SqrtStrategy) Singleton() bool { return false } + +// K returns floor(sqrt(n)). +func (SqrtStrategy) K(n *big.Int) []*big.Int { + sqrt := new(big.Int).Sqrt(n) + return []*big.Int{sqrt} +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/dict/dict.go b/vendor/github.com/mmcloughlin/addchain/alg/dict/dict.go new file mode 100644 index 00000000000..08a1fa0e0fe --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/dict/dict.go @@ -0,0 +1,434 @@ +// Package dict implements dictionary and run-length addition chain algorithms. +package dict + +import ( + "errors" + "fmt" + "math/big" + "sort" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/alg" + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/bigints" + "github.com/mmcloughlin/addchain/internal/bigvector" +) + +// References: +// +// [braueraddsubchains] Martin Otto. Brauer addition-subtraction chains. PhD thesis, Universitat +// Paderborn. 2001. +// http://www.martin-otto.de/publications/docs/2001_MartinOtto_Diplom_BrauerAddition-SubtractionChains.pdf +// [genshortchains] Kunihiro, Noboru and Yamamoto, Hirosuke. New Methods for Generating Short +// Addition Chains. IEICE Transactions on Fundamentals of Electronics +// Communications and Computer Sciences. 2000. +// https://pdfs.semanticscholar.org/b398/d10faca35af9ce5a6026458b251fd0a5640c.pdf +// [hehcc:exp] Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve +// Cryptography, chapter 9. 2006. +// http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf + +// Term represents the integer D * 2ᴱ. +type Term struct { + D *big.Int + E uint +} + +// Int converts the term to an integer. +func (t Term) Int() *big.Int { + return new(big.Int).Lsh(t.D, t.E) +} + +// Sum is the representation of an integer as a sum of dictionary terms. See +// [hehcc:exp] definition 9.34. +type Sum []Term + +// Int computes the dictionary sum as an integer. +func (s Sum) Int() *big.Int { + x := bigint.Zero() + for _, t := range s { + x.Add(x, t.Int()) + } + return x +} + +// SortByExponent sorts terms in ascending order of the exponent E. +func (s Sum) SortByExponent() { + sort.Slice(s, func(i, j int) bool { return s[i].E < s[j].E }) +} + +// Dictionary returns the distinct D values in the terms of this sum. The values +// are returned in ascending order. +func (s Sum) Dictionary() []*big.Int { + dict := make([]*big.Int, 0, len(s)) + for _, t := range s { + dict = append(dict, t.D) + } + bigints.Sort(dict) + return bigints.Unique(dict) +} + +// Decomposer is a method of breaking an integer into a dictionary sum. +type Decomposer interface { + Decompose(x *big.Int) Sum + String() string +} + +// FixedWindow breaks integers into k-bit windows. +type FixedWindow struct { + K uint // Window size. +} + +func (w FixedWindow) String() string { return fmt.Sprintf("fixed_window(%d)", w.K) } + +// Decompose represents x in terms of k-bit windows from left to right. +func (w FixedWindow) Decompose(x *big.Int) Sum { + sum := Sum{} + h := x.BitLen() + for h > 0 { + l := max(h-int(w.K), 0) + d := bigint.Extract(x, uint(l), uint(h)) + if bigint.IsNonZero(d) { + sum = append(sum, Term{D: d, E: uint(l)}) + } + h = l + } + sum.SortByExponent() + return sum +} + +// SlidingWindow breaks integers into k-bit windows, skipping runs of zeros +// where possible. See [hehcc:exp] section 9.1.3 or [braueraddsubchains] section +// 1.2.3. +type SlidingWindow struct { + K uint // Window size. +} + +func (w SlidingWindow) String() string { return fmt.Sprintf("sliding_window(%d)", w.K) } + +// Decompose represents x in base 2ᵏ. +func (w SlidingWindow) Decompose(x *big.Int) Sum { + sum := Sum{} + h := x.BitLen() - 1 + for h >= 0 { + // Find first 1. + for h >= 0 && x.Bit(h) == 0 { + h-- + } + + if h < 0 { + break + } + + // Look down k positions. + l := max(h-int(w.K)+1, 0) + + // Advance to the next 1. + for x.Bit(l) == 0 { + l++ + } + + sum = append(sum, Term{ + D: bigint.Extract(x, uint(l), uint(h+1)), + E: uint(l), + }) + + h = l - 1 + } + sum.SortByExponent() + return sum +} + +// RunLength decomposes integers in to runs of 1s up to a maximal length. See +// [genshortchains] Section 3.1. +type RunLength struct { + T uint // Maximal run length. Zero means no limit. +} + +func (r RunLength) String() string { return fmt.Sprintf("run_length(%d)", r.T) } + +// Decompose breaks x into runs of 1 bits. +func (r RunLength) Decompose(x *big.Int) Sum { + sum := Sum{} + i := x.BitLen() - 1 + for i >= 0 { + // Find first 1. + for i >= 0 && x.Bit(i) == 0 { + i-- + } + + if i < 0 { + break + } + + // Look for the end of the run. + s := i + for i >= 0 && x.Bit(i) == 1 && (r.T == 0 || uint(s-i) < r.T) { + i-- + } + + // We have a run from s to i+1. + sum = append(sum, Term{ + D: bigint.Ones(uint(s - i)), + E: uint(i + 1), + }) + } + sum.SortByExponent() + return sum +} + +// Hybrid is a mix of the sliding window and run length decomposition methods, +// similar to the "Hybrid Method" of [genshortchains] Section 3.3. +type Hybrid struct { + K uint // Window size. + T uint // Maximal run length. Zero means no limit. +} + +func (h Hybrid) String() string { return fmt.Sprintf("hybrid(%d,%d)", h.K, h.T) } + +// Decompose breaks x into k-bit sliding windows or runs of 1s up to length T. +func (h Hybrid) Decompose(x *big.Int) Sum { + sum := Sum{} + + // Clone since we'll be modifying it. + y := bigint.Clone(x) + + // Process runs of length at least K. + i := y.BitLen() - 1 + for i >= 0 { + // Find first 1. + for i >= 0 && y.Bit(i) == 0 { + i-- + } + + if i < 0 { + break + } + + // Look for the end of the run. + s := i + for i >= 0 && y.Bit(i) == 1 && (h.T == 0 || uint(s-i) < h.T) { + i-- + } + + // We have a run from s to i+1. Skip it if its short. + n := uint(s - i) + if n <= h.K { + continue + } + + // Add it to the sum and remove it from the integer. + sum = append(sum, Term{ + D: bigint.Ones(n), + E: uint(i + 1), + }) + + y.Xor(y, bigint.Mask(uint(i+1), uint(s+1))) + } + + // Process what remains with a sliding window. + w := SlidingWindow{K: h.K} + rem := w.Decompose(y) + + sum = append(sum, rem...) + sum.SortByExponent() + + return sum +} + +// Algorithm implements a general dictionary-based chain construction algorithm, +// as in [braueraddsubchains] Algorithm 1.26. This operates in three stages: +// decompose the target into a sum of dictionray terms, use a sequence algorithm +// to generate the dictionary, then construct the target from the dictionary +// terms. +type Algorithm struct { + decomp Decomposer + seqalg alg.SequenceAlgorithm +} + +// NewAlgorithm builds a dictionary algorithm that breaks up integers using the +// decomposer d and uses the sequence algorithm s to generate dictionary +// entries. +func NewAlgorithm(d Decomposer, a alg.SequenceAlgorithm) *Algorithm { + return &Algorithm{ + decomp: d, + seqalg: a, + } +} + +func (a Algorithm) String() string { + return fmt.Sprintf("dictionary(%s,%s)", a.decomp, a.seqalg) +} + +// FindChain builds an addition chain producing n. This works by using the +// configured Decomposer to represent n as a sum of dictionary terms, then +// delegating to the SequenceAlgorithm to build a chain producing the +// dictionary, and finally using the dictionary terms to construct n. See +// [genshortchains] Section 2 for a full description. +func (a Algorithm) FindChain(n *big.Int) (addchain.Chain, error) { + // Decompose the target. + sum := a.decomp.Decompose(n) + sum.SortByExponent() + + // Extract dictionary. + dict := sum.Dictionary() + + // Use the sequence algorithm to produce a chain for each element of the dictionary. + c, err := a.seqalg.FindSequence(dict) + if err != nil { + return nil, err + } + + // Reduce. + sum, c, err = primitive(sum, c) + if err != nil { + return nil, err + } + + // Build chain for n out of the dictionary. + dc := dictsumchain(sum) + c = append(c, dc...) + bigints.Sort(c) + c = addchain.Chain(bigints.Unique(c)) + + return c, nil +} + +// dictsumchain builds a chain for the integer represented by sum, assuming that +// all the terms of the sum are already present. Therefore this is intended to +// be appended to a chain that already contains the dictionary terms. +func dictsumchain(sum Sum) addchain.Chain { + c := addchain.Chain{} + k := len(sum) - 1 + cur := bigint.Clone(sum[k].D) + for ; k > 0; k-- { + // Shift until the next exponent. + for i := sum[k].E; i > sum[k-1].E; i-- { + cur.Lsh(cur, 1) + c.AppendClone(cur) + } + + // Add in the dictionary term at this position. + cur.Add(cur, sum[k-1].D) + c.AppendClone(cur) + } + + for i := sum[0].E; i > 0; i-- { + cur.Lsh(cur, 1) + c.AppendClone(cur) + } + + return c +} + +// primitive removes terms from the dictionary that are only required once. +// +// The general structure of dictionary based algorithm is to decompose the +// target into a sum of dictionary terms, then create a chain for the +// dictionary, and then create the target from that. In a case where a +// dictionary term is only required once in the target, this can cause extra +// work. In such a case, we will spend operations on creating the dictionary +// term independently, and then later add it into the result. Since it is only +// needed once, we can effectively construct the dictionary term "on the fly" as +// we build up the final target. +// +// This function looks for such opportunities. If it finds them it will produce +// an alternative dictionary sum that replaces that term with a sum of smaller +// terms. +func primitive(sum Sum, c addchain.Chain) (Sum, addchain.Chain, error) { + // This optimization cannot apply if the sum has only one term. + if len(sum) == 1 { + return sum, c, nil + } + + n := len(c) + + // We'll need a mapping from chain elements to where they appear in the chain. + idx := map[string]int{} + for i, x := range c { + idx[x.String()] = i + } + + // Build program for the chain. + p, err := c.Program() + if err != nil { + return nil, nil, err + } + + // How many times is each index read during construction, and during its use in the dictionary chain. + reads := p.ReadCounts() + + for _, t := range sum { + i := idx[t.D.String()] + reads[i]++ + } + + // Now, the primitive dictionary elements are those that are read at least twice, and their dependencies. + deps := p.Dependencies() + primitive := make([]bool, n) + + for i, numreads := range reads { + if numreads < 2 { + continue + } + primitive[i] = true + for _, j := range bigint.BitsSet(deps[i]) { + primitive[j] = true + } + } + + // Express every position in the chain as a linear combination of dictionary + // terms that are used more than once. + vc := []bigvector.Vector{bigvector.NewBasis(n, 0)} + for i, op := range p { + var next bigvector.Vector + if primitive[i+1] { + next = bigvector.NewBasis(n, i+1) + } else { + next = bigvector.Add(vc[op.I], vc[op.J]) + } + vc = append(vc, next) + } + + // Now express the target sum in terms that are used more than once. + v := bigvector.New(n) + for _, t := range sum { + i := idx[t.D.String()] + v = bigvector.Add(v, bigvector.Lsh(vc[i], t.E)) + } + + // Rebuild this into a dictionary sum. + out := Sum{} + for i := 0; i < v.Len(); i++ { + for _, e := range bigint.BitsSet(v.Idx(i)) { + out = append(out, Term{ + D: c[i], + E: uint(e), + }) + } + } + + out.SortByExponent() + + // We should have not changed the sum. + if !bigint.Equal(out.Int(), sum.Int()) { + return nil, nil, errors.New("reconstruction does not match") + } + + // Prune any elements of the chain that are used only once. + pruned := addchain.Chain{} + for i, x := range c { + if primitive[i] { + pruned = append(pruned, x) + } + } + + return out, pruned, nil +} + +// max returns the maximum of a and b. +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/dict/runs.go b/vendor/github.com/mmcloughlin/addchain/alg/dict/runs.go new file mode 100644 index 00000000000..5277be47643 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/dict/runs.go @@ -0,0 +1,108 @@ +package dict + +import ( + "errors" + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/alg" + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/bigints" +) + +// RunsAlgorithm is a custom variant of the dictionary approach that decomposes +// a target into runs of ones. It leverages the observation that building a +// dictionary consisting of runs of 1s of lengths l₁, l₂, ..., l_k can itself +// be reduced to first finding an addition chain for the run lengths. Then from +// this chain we can build a chain for the runs themselves. +type RunsAlgorithm struct { + seqalg alg.SequenceAlgorithm +} + +// NewRunsAlgorithm constructs a RunsAlgorithm using the given sequence +// algorithm to generate addition sequences for run lengths. Note that since run +// lengths are far smaller than the integers themselves, this sequence algorithm +// does not need to be able to handle large integers. +func NewRunsAlgorithm(a alg.SequenceAlgorithm) *RunsAlgorithm { + return &RunsAlgorithm{ + seqalg: a, + } +} + +func (a RunsAlgorithm) String() string { + return fmt.Sprintf("runs(%s)", a.seqalg) +} + +// FindChain uses the run lengths method to find a chain for n. +func (a RunsAlgorithm) FindChain(n *big.Int) (addchain.Chain, error) { + // Find the runs in n. + d := RunLength{T: 0} + sum := d.Decompose(n) + runs := sum.Dictionary() + + // Treat the run lengths themselves as a sequence to be solved. + lengths := []*big.Int{} + for _, run := range runs { + length := int64(run.BitLen()) + lengths = append(lengths, big.NewInt(length)) + } + + // Delegate to the sequence algorithm for a solution. + lc, err := a.seqalg.FindSequence(lengths) + if err != nil { + return nil, err + } + + // Build a dictionary chain from this. + c, err := RunsChain(lc) + if err != nil { + return nil, err + } + + // Reduce. + sum, c, err = primitive(sum, c) + if err != nil { + return nil, err + } + + // Build chain for n out of the dictionary. + dc := dictsumchain(sum) + c = append(c, dc...) + bigints.Sort(c) + c = addchain.Chain(bigints.Unique(c)) + + return c, nil +} + +// RunsChain takes a chain for the run lengths and generates a chain for the +// runs themselves. That is, if the provided chain is l₁, l₂, ..., l_k then +// the result will contain r(l₁), r(l₂), ..., r(l_k) where r(n) = 2ⁿ - 1. +func RunsChain(lc addchain.Chain) (addchain.Chain, error) { + p, err := lc.Program() + if err != nil { + return nil, err + } + + c := addchain.New() + s := map[uint]uint{} // current largest shift of each run length + for _, op := range p { + a, b := bigint.MinMax(lc[op.I], lc[op.J]) + if !a.IsUint64() || !b.IsUint64() { + return nil, errors.New("values in lengths chain are far too large") + } + + la := uint(a.Uint64()) + lb := uint(b.Uint64()) + + rb := bigint.Ones(lb) + for ; s[lb] < la; s[lb]++ { + shift := new(big.Int).Lsh(rb, s[lb]+1) + c = append(c, shift) + } + + c = append(c, bigint.Ones(la+lb)) + } + + return c, nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/ensemble/ensemble.go b/vendor/github.com/mmcloughlin/addchain/alg/ensemble/ensemble.go new file mode 100644 index 00000000000..f84a2626753 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/ensemble/ensemble.go @@ -0,0 +1,71 @@ +// Package ensemble provides a collection of addition chain algorithms intended +// for target integers of cryptographic interest. +package ensemble + +import ( + "github.com/mmcloughlin/addchain/alg" + "github.com/mmcloughlin/addchain/alg/contfrac" + "github.com/mmcloughlin/addchain/alg/dict" + "github.com/mmcloughlin/addchain/alg/heuristic" + "github.com/mmcloughlin/addchain/alg/opt" +) + +// Ensemble is a convenience for building an ensemble of chain algorithms intended for large integers. +func Ensemble() []alg.ChainAlgorithm { + // Choose sequence algorithms. + seqalgs := []alg.SequenceAlgorithm{ + heuristic.NewAlgorithm(heuristic.UseFirst( + heuristic.Halving{}, + heuristic.DeltaLargest{}, + )), + heuristic.NewAlgorithm(heuristic.UseFirst( + heuristic.Halving{}, + heuristic.Approximation{}, + )), + } + + for _, strategy := range contfrac.Strategies { + if strategy.Singleton() { + seqalgs = append(seqalgs, contfrac.NewAlgorithm(strategy)) + } + } + + // Build decomposers. + decomposers := []dict.Decomposer{} + for k := uint(4); k <= 128; k *= 2 { + decomposers = append(decomposers, dict.SlidingWindow{K: k}) + } + + decomposers = append(decomposers, dict.RunLength{T: 0}) + for t := uint(16); t <= 128; t *= 2 { + decomposers = append(decomposers, dict.RunLength{T: t}) + } + + for k := uint(2); k <= 8; k++ { + decomposers = append(decomposers, dict.Hybrid{K: k, T: 0}) + for t := uint(16); t <= 64; t *= 2 { + decomposers = append(decomposers, dict.Hybrid{K: k, T: t}) + } + } + + // Build dictionary algorithms for every combination. + as := []alg.ChainAlgorithm{} + for _, decomp := range decomposers { + for _, seqalg := range seqalgs { + a := dict.NewAlgorithm(decomp, seqalg) + as = append(as, a) + } + } + + // Add the runs algorithms. + for _, seqalg := range seqalgs { + as = append(as, dict.NewRunsAlgorithm(seqalg)) + } + + // Wrap in an optimization layer. + for i, a := range as { + as[i] = opt.Algorithm{Algorithm: a} + } + + return as +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/exec/exec.go b/vendor/github.com/mmcloughlin/addchain/alg/exec/exec.go new file mode 100644 index 00000000000..731aa29e176 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/exec/exec.go @@ -0,0 +1,99 @@ +// Package exec implements addition chain algorithm execution. +package exec + +import ( + "errors" + "io/ioutil" + "log" + "math/big" + "runtime" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/alg" + "github.com/mmcloughlin/addchain/internal/bigint" +) + +// Result from applying an algorithm to a target. +type Result struct { + Target *big.Int + Algorithm alg.ChainAlgorithm + Err error + Chain addchain.Chain + Program addchain.Program +} + +// Execute the algorithm on the target number n. +func Execute(n *big.Int, a alg.ChainAlgorithm) Result { + r := Result{ + Target: n, + Algorithm: a, + } + + r.Chain, r.Err = a.FindChain(n) + if r.Err != nil { + return r + } + + // Note this also performs validation. + r.Program, r.Err = r.Chain.Program() + if r.Err != nil { + return r + } + + // Still, verify that it produced what we wanted. + if !bigint.Equal(r.Chain.End(), n) { + r.Err = errors.New("did not produce the required value") + } + + return r +} + +// Parallel executes multiple algorithms in parallel. +type Parallel struct { + limit int + logger *log.Logger +} + +// NewParallel builds a new parallel executor. +func NewParallel() *Parallel { + return &Parallel{ + limit: runtime.NumCPU(), + logger: log.New(ioutil.Discard, "", 0), + } +} + +// SetConcurrency sets the number of algorithms that may be run in parallel. +func (p *Parallel) SetConcurrency(limit int) { + p.limit = limit +} + +// SetLogger sets logging output. +func (p *Parallel) SetLogger(l *log.Logger) { + p.logger = l +} + +// Execute all algorithms against the provided target. +func (p Parallel) Execute(n *big.Int, as []alg.ChainAlgorithm) []Result { + rs := make([]Result, len(as)) + + // Use buffered channel to limit concurrency. + type token struct{} + sem := make(chan token, p.limit) + + for i, a := range as { + sem <- token{} + go func(i int, a alg.ChainAlgorithm) { + p.logger.Printf("start: %s", a) + rs[i] = Execute(n, a) + p.logger.Printf("done: %s", a) + <-sem + }(i, a) + } + + // Wait for completion. + for i := 0; i < p.limit; i++ { + sem <- token{} + } + + return rs +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/heuristic/heuristic.go b/vendor/github.com/mmcloughlin/addchain/alg/heuristic/heuristic.go new file mode 100644 index 00000000000..94abb6400a6 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/heuristic/heuristic.go @@ -0,0 +1,234 @@ +// Package heuristic implements heuristic-based addition sequence algorithms +// with the Bos-Coster Makesequence structure. +package heuristic + +import ( + "errors" + "fmt" + "math/big" + "strings" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/bigints" +) + +// References: +// +// [boscoster] Bos, Jurjen and Coster, Matthijs. Addition Chain Heuristics. In Advances in +// Cryptology --- CRYPTO' 89 Proceedings, pages 400--407. 1990. +// https://link.springer.com/content/pdf/10.1007/0-387-34805-0_37.pdf +// [github:kwantam/addchain] Riad S. Wahby. kwantam/addchain. Github Repository. Apache License, Version 2.0. +// 2018. https://github.com/kwantam/addchain +// [hehcc:exp] Christophe Doche. Exponentiation. Handbook of Elliptic and Hyperelliptic Curve +// Cryptography, chapter 9. 2006. +// http://koclab.cs.ucsb.edu/teaching/ecc/eccPapers/Doche-ch09.pdf +// [modboscoster] Ayan Nandy. Modifications of Bos and Coster’s Heuristics in search of a +// shorter addition chain for faster exponentiation. Masters thesis, Indian +// Statistical Institute Kolkata. 2011. +// http://library.isical.ac.in:8080/jspui/bitstream/10263/6441/1/DISS-285.pdf +// [mpnt] F. L. Ţiplea, S. Iftene, C. Hriţcu, I. Goriac, R. Gordân and E. Erbiceanu. +// MpNT: A Multi-Precision Number Theory Package, Number Theoretical Algorithms +// (I). Technical Report TR03-02, Faculty of Computer Science, "Alexandru Ioan +// Cuza" University, Iasi. 2003. https://profs.info.uaic.ro/~tr/tr03-02.pdf +// [speedsubgroup] Stam, Martijn. Speeding up subgroup cryptosystems. PhD thesis, Technische +// Universiteit Eindhoven. 2003. https://cr.yp.to/bib/2003/stam-thesis.pdf + +// Heuristic suggests insertions given a current protosequence. +type Heuristic interface { + // Suggest insertions given a target and protosequence f. Protosequence must + // contain sorted distinct integers. + Suggest(f []*big.Int, target *big.Int) []*big.Int + + // String returns a name for the heuristic. + String() string +} + +// Algorithm searches for an addition sequence using a heuristic at each step. +// This implements the framework given in [mpnt], page 63, with the heuristic +// playing the role of the "newnumbers" function. +type Algorithm struct { + heuristic Heuristic +} + +// NewAlgorithm builds a heuristic algorithm. +func NewAlgorithm(h Heuristic) *Algorithm { + return &Algorithm{ + heuristic: h, + } +} + +func (h Algorithm) String() string { + return fmt.Sprintf("heuristic(%v)", h.heuristic) +} + +// FindSequence searches for an addition sequence for the given targets. +func (h Algorithm) FindSequence(targets []*big.Int) (addchain.Chain, error) { + // Skip the special case when targets is just {1}. + if len(targets) == 1 && bigint.EqualInt64(targets[0], 1) { + return targets, nil + } + + // Initialize protosequence. + leader := bigints.Int64s(1, 2) + proto := append(leader, targets...) + bigints.Sort(proto) + proto = bigints.Unique(proto) + c := []*big.Int{} + + for len(proto) > 2 { + // Pop the target element. + top := len(proto) - 1 + target := proto[top] + proto = proto[:top] + c = bigints.InsertSortedUnique(c, target) + + // Apply heuristic. + insert := h.heuristic.Suggest(proto, target) + if insert == nil { + return nil, errors.New("failed to find sequence") + } + + // Update protosequence. + proto = bigints.MergeUnique(proto, insert) + } + + // Prepare the chain to return. + c = bigints.MergeUnique(leader, c) + + return addchain.Chain(c), nil +} + +// DeltaLargest implements the simple heuristic of adding the delta between the +// largest two entries in the protosequence. +type DeltaLargest struct{} + +func (DeltaLargest) String() string { return "delta_largest" } + +// Suggest proposes inserting target-max(f). +func (DeltaLargest) Suggest(f []*big.Int, target *big.Int) []*big.Int { + n := len(f) + delta := new(big.Int).Sub(target, f[n-1]) + if delta.Sign() <= 0 { + panic("delta must be positive") + } + return []*big.Int{delta} +} + +// Approximation is the "Approximation" heuristic from [boscoster]. +type Approximation struct{} + +func (Approximation) String() string { return "approximation" } + +// Suggest applies the "Approximation" heuristic. This heuristic looks for two +// elements a, b in the list that sum to something close to the target element +// f. That is, we look for f-(a+b) = epsilon where a ⩽ b and epsilon is a +// "small" positive value. +func (Approximation) Suggest(f []*big.Int, target *big.Int) []*big.Int { + delta := new(big.Int) + insert := new(big.Int) + mindelta := new(big.Int) + best := new(big.Int) + first := true + + // Leverage the fact that f contains sorted distinct integers to apply a + // linear algorithm, similar to the 2-SUM problem. Maintain left and right + // pointers and adjust them based on whether the sum is above or below the + // target. + for l, r := 0, len(f)-1; l <= r; { + a, b := f[l], f[r] + + // Compute the delta f-(a+b). + delta.Add(a, b) + delta.Sub(target, delta) + if delta.Sign() < 0 { + // Sum exceeds target, decrement r for smaller b value. + r-- + continue + } + + // Proposed insertion is a+delta. + insert.Add(a, delta) + + // If it's actually in the sequence already, use it. + if bigints.ContainsSorted(insert, f) { + return []*big.Int{insert} + } + + // Keep it if its the closest we've seen. + if first || delta.Cmp(mindelta) < 0 { + mindelta.Set(delta) + best.Set(insert) + first = false + } + + // Advance to next a value. + l++ + } + + return []*big.Int{best} +} + +// Halving is the "Halving" heuristic from [boscoster]. +type Halving struct{} + +func (Halving) String() string { return "halving" } + +// Suggest applies when the target is at least twice as big as the next largest. +// If so it will return a sequence of doublings to insert. Otherwise it will +// return nil. +func (Halving) Suggest(f []*big.Int, target *big.Int) []*big.Int { + n := len(f) + max, next := target, f[n-1] + + // Check the condition f / f₁ ⩾ 2ᵘ + r := new(big.Int).Div(max, next) + if r.BitLen() < 2 { + return nil + } + u := r.BitLen() - 1 + + // Compute k = floor( f / 2ᵘ ). + k := new(big.Int).Rsh(max, uint(u)) + + // Proposal to insert: + // Delta d = f - k*2ᵘ + // Sequence k, 2*k, ..., k*2ᵘ + kshifts := []*big.Int{} + for e := 0; e <= u; e++ { + kshift := new(big.Int).Lsh(k, uint(e)) + kshifts = append(kshifts, kshift) + } + d := new(big.Int).Sub(max, kshifts[u]) + if bigint.IsZero(d) { + return kshifts[:u] + } + + return bigints.InsertSortedUnique(kshifts, d) +} + +// UseFirst builds a compositite heuristic that will make the first non-nil +// suggestion from the sub-heuristics. +func UseFirst(heuristics ...Heuristic) Heuristic { + return useFirst(heuristics) +} + +type useFirst []Heuristic + +func (h useFirst) String() string { + names := []string{} + for _, sub := range h { + names = append(names, sub.String()) + } + return "use_first(" + strings.Join(names, ",") + ")" +} + +// Suggest delegates to each sub-heuristic in turn and returns the first non-nil suggestion. +func (h useFirst) Suggest(f []*big.Int, target *big.Int) []*big.Int { + for _, heuristic := range h { + if insert := heuristic.Suggest(f, target); insert != nil { + return insert + } + } + return nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/alg/opt/opt.go b/vendor/github.com/mmcloughlin/addchain/alg/opt/opt.go new file mode 100644 index 00000000000..bf28cb9062a --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/alg/opt/opt.go @@ -0,0 +1,101 @@ +// Package opt implements generic optimizations that remove redundancy from addition chains. +package opt + +import ( + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain" + "github.com/mmcloughlin/addchain/alg" +) + +// Algorithm applies chain optimization to the result of a wrapped algorithm. +type Algorithm struct { + Algorithm alg.ChainAlgorithm +} + +func (a Algorithm) String() string { + return fmt.Sprintf("opt(%s)", a.Algorithm) +} + +// FindChain delegates to the wrapped algorithm, then runs Optimize on the result. +func (a Algorithm) FindChain(n *big.Int) (addchain.Chain, error) { + c, err := a.Algorithm.FindChain(n) + if err != nil { + return nil, err + } + + opt, err := Optimize(c) + if err != nil { + return nil, err + } + + return opt, nil +} + +// Optimize aims to remove redundancy from an addition chain. +func Optimize(c addchain.Chain) (addchain.Chain, error) { + // Build program for c with all possible options at each step. + ops := make([][]addchain.Op, len(c)) + for k := 1; k < len(c); k++ { + ops[k] = c.Ops(k) + } + + // Count how many times each index is used where it is the only available Op. + counts := make([]int, len(c)) + for k := 1; k < len(c); k++ { + if len(ops[k]) != 1 { + continue + } + for _, i := range ops[k][0].Operands() { + counts[i]++ + } + } + + // Now, try to remove the positions which are never the only available op. + remove := []int{} + for k := 1; k < len(c)-1; k++ { + if counts[k] > 0 { + continue + } + + // Prune places k is used. + for l := k + 1; l < len(c); l++ { + ops[l] = pruneuses(ops[l], k) + + // If this list now only has one element, the operands in it are now + // indispensable. + if len(ops[l]) == 1 { + for _, i := range ops[l][0].Operands() { + counts[i]++ + } + } + } + + // Mark k for deletion. + remove = append(remove, k) + } + + // Perform removals. + pruned := addchain.Chain{} + for i, x := range c { + if len(remove) > 0 && remove[0] == i { + remove = remove[1:] + continue + } + pruned = append(pruned, x) + } + + return pruned, nil +} + +// pruneuses removes any uses of i from the list of operations. +func pruneuses(ops []addchain.Op, i int) []addchain.Op { + filtered := ops[:0] + for _, op := range ops { + if !op.Uses(i) { + filtered = append(filtered, op) + } + } + return filtered +} diff --git a/vendor/github.com/mmcloughlin/addchain/chain.go b/vendor/github.com/mmcloughlin/addchain/chain.go new file mode 100644 index 00000000000..3d41657ebc0 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/chain.go @@ -0,0 +1,194 @@ +// Package addchain provides addition chain types and operations on them. +package addchain + +import ( + "errors" + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain/internal/bigint" + "github.com/mmcloughlin/addchain/internal/bigints" +) + +// References: +// +// [efficientcompaddchain] Bergeron, F., Berstel, J. and Brlek, S. Efficient computation of addition +// chains. Journal de theorie des nombres de Bordeaux. 1994. +// http://www.numdam.org/item/JTNB_1994__6_1_21_0 +// [knuth] Knuth, Donald E. Evaluation of Powers. The Art of Computer Programming, Volume 2 +// (Third Edition): Seminumerical Algorithms, chapter 4.6.3. 1997. +// https://www-cs-faculty.stanford.edu/~knuth/taocp.html + +// Chain is an addition chain. +type Chain []*big.Int + +// New constructs the minimal chain {1}. +func New() Chain { + return Chain{big.NewInt(1)} +} + +// Int64s builds a chain from the given int64 values. +func Int64s(xs ...int64) Chain { + return Chain(bigints.Int64s(xs...)) +} + +// Clone the chain. +func (c Chain) Clone() Chain { + return bigints.Clone(c) +} + +// AppendClone appends a copy of x to c. +func (c *Chain) AppendClone(x *big.Int) { + *c = append(*c, bigint.Clone(x)) +} + +// End returns the last element of the chain. +func (c Chain) End() *big.Int { + return c[len(c)-1] +} + +// Ops returns all operations that produce the kth position. This could be empty +// for an invalid chain. +func (c Chain) Ops(k int) []Op { + ops := []Op{} + s := new(big.Int) + + // If the prefix is ascending this can be done in linear time. + if c[:k].IsAscending() { + for l, r := 0, k-1; l <= r; { + s.Add(c[l], c[r]) + cmp := s.Cmp(c[k]) + if cmp == 0 { + ops = append(ops, Op{l, r}) + } + if cmp <= 0 { + l++ + } else { + r-- + } + } + return ops + } + + // Fallback to quadratic. + for i := 0; i < k; i++ { + for j := i; j < k; j++ { + s.Add(c[i], c[j]) + if s.Cmp(c[k]) == 0 { + ops = append(ops, Op{i, j}) + } + } + } + + return ops +} + +// Op returns an Op that produces the kth position. +func (c Chain) Op(k int) (Op, error) { + ops := c.Ops(k) + if len(ops) == 0 { + return Op{}, fmt.Errorf("position %d is not the sum of previous entries", k) + } + return ops[0], nil +} + +// Program produces a program that generates the chain. +func (c Chain) Program() (Program, error) { + // Sanity checks. + if len(c) == 0 { + return nil, errors.New("chain empty") + } + + if c[0].Cmp(big.NewInt(1)) != 0 { + return nil, errors.New("chain must start with 1") + } + + if bigints.Contains(bigint.Zero(), c) { + return nil, errors.New("chain contains zero") + } + + for i := 0; i < len(c); i++ { + for j := i + 1; j < len(c); j++ { + if bigint.Equal(c[i], c[j]) { + return nil, fmt.Errorf("chain contains duplicate: %v at positions %d and %d", c[i], i, j) + } + } + } + + // Produce the program. + p := Program{} + for k := 1; k < len(c); k++ { + op, err := c.Op(k) + if err != nil { + return nil, err + } + p = append(p, op) + } + + return p, nil +} + +// Validate checks that c is in fact an addition chain. +func (c Chain) Validate() error { + _, err := c.Program() + return err +} + +// Produces checks that c is a valid chain ending with target. +func (c Chain) Produces(target *big.Int) error { + if err := c.Validate(); err != nil { + return err + } + if c.End().Cmp(target) != 0 { + return errors.New("chain does not end with target") + } + return nil +} + +// Superset checks that c is a valid chain containing all the targets. +func (c Chain) Superset(targets []*big.Int) error { + if err := c.Validate(); err != nil { + return err + } + for _, target := range targets { + if !bigints.Contains(target, c) { + return fmt.Errorf("chain does not contain %v", target) + } + } + return nil +} + +// IsAscending reports whether the chain is ascending, that is if it's in sorted +// order without repeats, as defined in [knuth] Section 4.6.3 formula (11). +// Does not fully validate the chain, only that it is ascending. +func (c Chain) IsAscending() bool { + if len(c) == 0 || !bigint.EqualInt64(c[0], 1) { + return false + } + for i := 1; i < len(c); i++ { + if c[i-1].Cmp(c[i]) >= 0 { + return false + } + } + return true +} + +// Product computes the product of two addition chains. The is the "o times" +// operator defined in [efficientcompaddchain] Section 2. +func Product(a, b Chain) Chain { + c := a.Clone() + last := c.End() + for _, x := range b[1:] { + y := new(big.Int).Mul(last, x) + c = append(c, y) + } + return c +} + +// Plus adds x to the addition chain. This is the "o plus" operator defined in +// [efficientcompaddchain] Section 2. +func Plus(a Chain, x *big.Int) Chain { + c := a.Clone() + y := new(big.Int).Add(c.End(), x) + return append(c, y) +} diff --git a/vendor/github.com/mmcloughlin/addchain/codecov.yml b/vendor/github.com/mmcloughlin/addchain/codecov.yml new file mode 100644 index 00000000000..35cde5cd5e8 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/codecov.yml @@ -0,0 +1,4 @@ +coverage: + status: + project: off + patch: off diff --git a/vendor/github.com/mmcloughlin/addchain/go.mod b/vendor/github.com/mmcloughlin/addchain/go.mod new file mode 100644 index 00000000000..fc91d5d274d --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/go.mod @@ -0,0 +1,8 @@ +module github.com/mmcloughlin/addchain + +go 1.16 + +require ( + github.com/google/subcommands v1.2.0 + github.com/mmcloughlin/profile v0.1.1 +) diff --git a/vendor/github.com/mmcloughlin/addchain/go.sum b/vendor/github.com/mmcloughlin/addchain/go.sum new file mode 100644 index 00000000000..0111b2abf38 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/go.sum @@ -0,0 +1,4 @@ +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/mmcloughlin/profile v0.1.1 h1:jhDmAqPyebOsVDOCICJoINoLb/AnLBaUw58nFzxWS2w= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= diff --git a/vendor/github.com/mmcloughlin/addchain/install.sh b/vendor/github.com/mmcloughlin/addchain/install.sh new file mode 100644 index 00000000000..6ea84cbc9e9 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/install.sh @@ -0,0 +1,378 @@ +#!/bin/sh +set -e +# Code generated by godownloader. DO NOT EDIT. +# + +usage() { + this=$1 + cat </dev/null +} +echoerr() { + echo "$@" 1>&2 +} +log_prefix() { + echo "$0" +} +_logp=6 +log_set_priority() { + _logp="$1" +} +log_priority() { + if test -z "$1"; then + echo "$_logp" + return + fi + [ "$1" -le "$_logp" ] +} +log_tag() { + case $1 in + 0) echo "emerg" ;; + 1) echo "alert" ;; + 2) echo "crit" ;; + 3) echo "err" ;; + 4) echo "warning" ;; + 5) echo "notice" ;; + 6) echo "info" ;; + 7) echo "debug" ;; + *) echo "$1" ;; + esac +} +log_debug() { + log_priority 7 || return 0 + echoerr "$(log_prefix)" "$(log_tag 7)" "$@" +} +log_info() { + log_priority 6 || return 0 + echoerr "$(log_prefix)" "$(log_tag 6)" "$@" +} +log_err() { + log_priority 3 || return 0 + echoerr "$(log_prefix)" "$(log_tag 3)" "$@" +} +log_crit() { + log_priority 2 || return 0 + echoerr "$(log_prefix)" "$(log_tag 2)" "$@" +} +uname_os() { + os=$(uname -s | tr '[:upper:]' '[:lower:]') + case "$os" in + cygwin_nt*) os="windows" ;; + mingw*) os="windows" ;; + msys_nt*) os="windows" ;; + esac + echo "$os" +} +uname_arch() { + arch=$(uname -m) + case $arch in + x86_64) arch="amd64" ;; + x86) arch="386" ;; + i686) arch="386" ;; + i386) arch="386" ;; + aarch64) arch="arm64" ;; + armv5*) arch="armv5" ;; + armv6*) arch="armv6" ;; + armv7*) arch="armv7" ;; + esac + echo ${arch} +} +uname_os_check() { + os=$(uname_os) + case "$os" in + darwin) return 0 ;; + dragonfly) return 0 ;; + freebsd) return 0 ;; + linux) return 0 ;; + android) return 0 ;; + nacl) return 0 ;; + netbsd) return 0 ;; + openbsd) return 0 ;; + plan9) return 0 ;; + solaris) return 0 ;; + windows) return 0 ;; + esac + log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib" + return 1 +} +uname_arch_check() { + arch=$(uname_arch) + case "$arch" in + 386) return 0 ;; + amd64) return 0 ;; + arm64) return 0 ;; + armv5) return 0 ;; + armv6) return 0 ;; + armv7) return 0 ;; + ppc64) return 0 ;; + ppc64le) return 0 ;; + mips) return 0 ;; + mipsle) return 0 ;; + mips64) return 0 ;; + mips64le) return 0 ;; + s390x) return 0 ;; + amd64p32) return 0 ;; + esac + log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib" + return 1 +} +untar() { + tarball=$1 + case "${tarball}" in + *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" ;; + *.tar) tar --no-same-owner -xf "${tarball}" ;; + *.zip) unzip "${tarball}" ;; + *) + log_err "untar unknown archive format for ${tarball}" + return 1 + ;; + esac +} +http_download_curl() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + code=$(curl -w '%{http_code}' -sL -o "$local_file" "$source_url") + else + code=$(curl -w '%{http_code}' -sL -H "$header" -o "$local_file" "$source_url") + fi + if [ "$code" != "200" ]; then + log_debug "http_download_curl received HTTP status $code" + return 1 + fi + return 0 +} +http_download_wget() { + local_file=$1 + source_url=$2 + header=$3 + if [ -z "$header" ]; then + wget -q -O "$local_file" "$source_url" + else + wget -q --header "$header" -O "$local_file" "$source_url" + fi +} +http_download() { + log_debug "http_download $2" + if is_command curl; then + http_download_curl "$@" + return + elif is_command wget; then + http_download_wget "$@" + return + fi + log_crit "http_download unable to find wget or curl" + return 1 +} +http_copy() { + tmp=$(mktemp) + http_download "${tmp}" "$1" "$2" || return 1 + body=$(cat "$tmp") + rm -f "${tmp}" + echo "$body" +} +github_release() { + owner_repo=$1 + version=$2 + test -z "$version" && version="latest" + giturl="https://github.com/${owner_repo}/releases/${version}" + json=$(http_copy "$giturl" "Accept:application/json") + test -z "$json" && return 1 + version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//') + test -z "$version" && return 1 + echo "$version" +} +hash_sha256() { + TARGET=${1:-/dev/stdin} + if is_command gsha256sum; then + hash=$(gsha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command sha256sum; then + hash=$(sha256sum "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command shasum; then + hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1 + echo "$hash" | cut -d ' ' -f 1 + elif is_command openssl; then + hash=$(openssl -dst openssl dgst -sha256 "$TARGET") || return 1 + echo "$hash" | cut -d ' ' -f a + else + log_crit "hash_sha256 unable to find command to compute sha-256 hash" + return 1 + fi +} +hash_sha256_verify() { + TARGET=$1 + checksums=$2 + if [ -z "$checksums" ]; then + log_err "hash_sha256_verify checksum file not specified in arg2" + return 1 + fi + BASENAME=${TARGET##*/} + want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1) + if [ -z "$want" ]; then + log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'" + return 1 + fi + got=$(hash_sha256 "$TARGET") + if [ "$want" != "$got" ]; then + log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got" + return 1 + fi +} +cat /dev/null <= 0 +} + +// ContainsSorted reports whether n is in xs, which is assumed to be sorted. +func ContainsSorted(n *big.Int, xs []*big.Int) bool { + i := sort.Search(len(xs), func(i int) bool { return xs[i].Cmp(n) >= 0 }) + return i < len(xs) && bigint.Equal(xs[i], n) +} + +// Clone a list of integers. +func Clone(xs []*big.Int) []*big.Int { + return append([]*big.Int{}, xs...) +} + +// Unique removes consecutive duplicates. +func Unique(xs []*big.Int) []*big.Int { + if len(xs) == 0 { + return []*big.Int{} + } + u := make([]*big.Int, 1, len(xs)) + u[0] = xs[0] + for _, x := range xs[1:] { + last := u[len(u)-1] + if !bigint.Equal(x, last) { + u = append(u, x) + } + } + return u +} + +// InsertSortedUnique inserts an integer into a slice of sorted distinct +// integers. +func InsertSortedUnique(xs []*big.Int, x *big.Int) []*big.Int { + return MergeUnique([]*big.Int{x}, xs) +} + +// MergeUnique merges two slices of sorted distinct integers. Elements in both +// slices are deduplicated. +func MergeUnique(xs, ys []*big.Int) []*big.Int { + r := make([]*big.Int, 0, len(xs)+len(ys)) + + for len(xs) > 0 && len(ys) > 0 { + switch xs[0].Cmp(ys[0]) { + case -1: + r = append(r, xs[0]) + xs = xs[1:] + case 0: + r = append(r, xs[0]) + xs = xs[1:] + ys = ys[1:] + case 1: + r = append(r, ys[0]) + ys = ys[1:] + } + } + + r = append(r, xs...) + r = append(r, ys...) + + return r +} diff --git a/vendor/github.com/mmcloughlin/addchain/internal/bigvector/bigvector.go b/vendor/github.com/mmcloughlin/addchain/internal/bigvector/bigvector.go new file mode 100644 index 00000000000..bac75bd5f8c --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/internal/bigvector/bigvector.go @@ -0,0 +1,86 @@ +// Package bigvector implements operations on vectors of immutable +// multi-precision integers. +package bigvector + +import ( + "math/big" + + "github.com/mmcloughlin/addchain/internal/bigint" +) + +// Vector of immutable multi-precision integers. +type Vector interface { + // Len returns vector length. + Len() int + + // Idx returns integer at index i. Returned integer must not be written to. + Idx(i int) *big.Int +} + +// New constructs an n-dimensional zero vector. +func New(n int) Vector { + return make(vector, n) +} + +type vector []big.Int + +func (v vector) Len() int { return len(v) } +func (v vector) Idx(i int) *big.Int { return &v[i] } + +// NewBasis constructs an n-dimensional basis vector with a 1 in position i. +func NewBasis(n, i int) Vector { + return basis{n: n, i: i} +} + +// Basis implementation saves allocations by returning pre-allocated zero and +// one integers based on the index requested. +var ( + zero = bigint.Zero() + one = bigint.One() +) + +type basis struct { + n int + i int +} + +func (b basis) Len() int { return b.n } + +func (b basis) Idx(i int) *big.Int { + switch { + case i >= b.n: + panic("bigvector: index out of range") + case i == b.i: + return one + default: + return zero + } +} + +// Add vectors. +func Add(u, v Vector) Vector { + assertsamelen(u, v) + n := u.Len() + w := make(vector, n) + for i := 0; i < n; i++ { + w[i].Add(u.Idx(i), v.Idx(i)) + } + return w +} + +// Lsh left shifts every element of the vector v. +func Lsh(v Vector, s uint) Vector { + n := v.Len() + w := make(vector, n) + for i := 0; i < n; i++ { + w[i].Lsh(v.Idx(i), s) + } + return w +} + +// assertsamelen panics if u and v are different lengths. +func assertsamelen(u, v Vector) { + if u.Len() != v.Len() { + panic("bigvector: length mismatch") + } +} diff --git a/vendor/github.com/mmcloughlin/addchain/internal/container/heap/heap.go b/vendor/github.com/mmcloughlin/addchain/internal/container/heap/heap.go new file mode 100644 index 00000000000..9d396f50e28 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/internal/container/heap/heap.go @@ -0,0 +1,55 @@ +// Package heap implements a heap on specific types. +package heap + +import "container/heap" + +// MinInts is a min-heap of integers. +type MinInts struct { + h *intheap +} + +// NewMinInts builds an empty integer min-heap. +func NewMinInts() *MinInts { + return &MinInts{ + h: &intheap{}, + } +} + +// Empty returns whether the heap is empty. +func (h *MinInts) Empty() bool { + return h.Len() == 0 +} + +// Len returns the number of elements in the heap. +func (h *MinInts) Len() int { + return h.h.Len() +} + +// Push x onto the heap. +func (h *MinInts) Push(x int) { + heap.Push(h.h, x) +} + +// Pop the min element from the heap. +func (h *MinInts) Pop() int { + return heap.Pop(h.h).(int) +} + +type intheap struct { + x []int +} + +func (h intheap) Len() int { return len(h.x) } +func (h intheap) Less(i, j int) bool { return h.x[i] < h.x[j] } +func (h intheap) Swap(i, j int) { h.x[i], h.x[j] = h.x[j], h.x[i] } + +func (h *intheap) Push(x interface{}) { + h.x = append(h.x, x.(int)) +} + +func (h *intheap) Pop() interface{} { + n := len(h.x) + x := h.x[n-1] + h.x = h.x[:n-1] + return x +} diff --git a/vendor/github.com/mmcloughlin/addchain/internal/errutil/errutil.go b/vendor/github.com/mmcloughlin/addchain/internal/errutil/errutil.go new file mode 100644 index 00000000000..364724118ce --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/internal/errutil/errutil.go @@ -0,0 +1,28 @@ +// Package errutil implements common error types and helper functions. +package errutil + +import ( + "fmt" + "io" +) + +// AssertionFailure is used for an error resulting from the failure of an +// expected invariant. +func AssertionFailure(format string, args ...interface{}) error { + return fmt.Errorf("assertion failure: "+format, args...) +} + +// UnexpectedType builds an error for an unexpected type, typically in a type switch. +func UnexpectedType(t interface{}) error { + return AssertionFailure("unexpected type %T", t) +} + +// CheckClose closes c. If an error occurs it will be written to the error +// pointer errp, if it doesn't already reference an error. This is intended to +// allow you to properly check errors when defering a close call. In this case +// the error pointer should be the address of a named error return. +func CheckClose(errp *error, c io.Closer) { + if err := c.Close(); err != nil && *errp == nil { + *errp = err + } +} diff --git a/vendor/github.com/mmcloughlin/addchain/internal/print/printer.go b/vendor/github.com/mmcloughlin/addchain/internal/print/printer.go new file mode 100644 index 00000000000..39bd4924977 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/internal/print/printer.go @@ -0,0 +1,104 @@ +// Package print provides helpers for structured output printing. +package print + +import ( + "fmt" + "io" + "strings" + "text/tabwriter" +) + +// DefaultIndent is the default string for one level of indentation. +const DefaultIndent = "\t" + +// Printer provides convenience methods for structured output printing. +// Specifically it stores any errors encountered so error checking does not have +// to be done on every print call. Also provides helpers for managing indentation. +type Printer struct { + out io.Writer + level int // current indentation level + indent string // indentation string + pending bool // if there's a pending indentation + err error // saved error from printing +} + +// New builds a printer writing to w. +func New(w io.Writer) Printer { + return Printer{ + out: w, + indent: DefaultIndent, + } +} + +// SetIndentString configures the string used for one level of indentation. +func (p *Printer) SetIndentString(indent string) { + p.indent = indent +} + +// Indent by one level. +func (p *Printer) Indent() { + p.level++ +} + +// Dedent by one level. +func (p *Printer) Dedent() { + p.level-- +} + +// Linef prints a formatted line. +func (p *Printer) Linef(format string, args ...interface{}) { + p.Printf(format, args...) + p.NL() +} + +// NL prints a newline. +func (p *Printer) NL() { + p.Printf("\n") + p.pending = true +} + +// Printf prints formatted output. +func (p *Printer) Printf(format string, args ...interface{}) { + if p.err != nil { + return + } + if p.pending { + indent := strings.Repeat(p.indent, p.level) + format = indent + format + p.pending = false + } + _, err := fmt.Fprintf(p.out, format, args...) + p.SetError(err) +} + +// Error returns the first error that occurred so far, if any. +func (p *Printer) Error() error { + return p.err +} + +// SetError records a possible error. +func (p *Printer) SetError(err error) { + if p.err == nil { + p.err = err + } +} + +// TabWriter provides tabwriter.Writer functionality with the Printer interface. +type TabWriter struct { + tw *tabwriter.Writer + Printer +} + +// NewTabWriter builds a TabWriter. Arguments are the same as for tabwriter.NewWriter. +func NewTabWriter(w io.Writer, minwidth, tabwidth, padding int, padchar byte, flags uint) *TabWriter { + tw := tabwriter.NewWriter(w, minwidth, tabwidth, padding, padchar, flags) + return &TabWriter{ + tw: tw, + Printer: New(tw), + } +} + +// Flush the tabwriter. +func (p *TabWriter) Flush() { + p.SetError(p.tw.Flush()) +} diff --git a/vendor/github.com/mmcloughlin/addchain/logo.svg b/vendor/github.com/mmcloughlin/addchain/logo.svg new file mode 100644 index 00000000000..0ab26295c04 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/vendor/github.com/mmcloughlin/addchain/meta/cite.go b/vendor/github.com/mmcloughlin/addchain/meta/cite.go new file mode 100644 index 00000000000..bfaae04c759 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/meta/cite.go @@ -0,0 +1,61 @@ +package meta + +import ( + "bytes" + "errors" + "fmt" + "io" + "strconv" + "strings" + + "github.com/mmcloughlin/addchain/internal/print" +) + +// CheckCitable checks whether a citation can be generated for this built +// version. +func (p *Properties) CheckCitable() error { + if !p.IsRelease() { + return errors.New("cannot cite non-release version") + } + return nil +} + +// WriteCitation writes BibTeX citation for the most recent release to the given +// writer. +func (p *Properties) WriteCitation(w io.Writer) error { + // Determine release time. + date, err := p.ReleaseTime() + if err != nil { + return fmt.Errorf("release date: %w", err) + } + + // Use tabwriter for field alignment. + tw := print.NewTabWriter(w, 1, 4, 1, ' ', 0) + + field := func(key, value string) { tw.Linef(" %s\t=\t%s,", key, value) } + str := func(key, value string) { field(key, "{"+value+"}") } + + tw.Linef("@misc{%s,", p.Name) + str("title", p.Title()) + str("author", "Michael B. McLoughlin") + field("year", strconv.Itoa(date.Year())) + field("month", strings.ToLower(date.Month().String()[:3])) + str("howpublished", "Repository \\url{"+p.RepositoryURL()+"}") + str("version", p.ReleaseVersion) + str("license", "BSD 3-Clause License") + str("doi", p.DOI) + str("url", p.DOIURL()) + tw.Linef("}") + tw.Flush() + + return tw.Error() +} + +// Citation returns a BibTeX citation for the most recent release. +func (p *Properties) Citation() (string, error) { + buf := bytes.NewBuffer(nil) + if err := p.WriteCitation(buf); err != nil { + return "", err + } + return buf.String(), nil +} diff --git a/vendor/github.com/mmcloughlin/addchain/meta/meta.go b/vendor/github.com/mmcloughlin/addchain/meta/meta.go new file mode 100644 index 00000000000..da8b0e2a982 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/meta/meta.go @@ -0,0 +1,101 @@ +// Package meta defines properties about this project. +package meta + +import ( + "fmt" + "path" + "time" +) + +// VersionTagPrefix is the prefix used on Git tags corresponding to semantic +// version releases. +const VersionTagPrefix = "v" + +// Properties about this software package. +type Properties struct { + // Name is the project name. + Name string + + // FullName is the "owner/name" identifier for the project. + FullName string + + // Description is the concise project headline. + Description string + + // BuildVersion is the version that was built. Typically populated at build + // time and will typically be empty for non-release builds. + BuildVersion string + + // ReleaseVersion is the version of the most recent release. + ReleaseVersion string + + // ReleaseDate is the date of the most recent release. (RFC3339 date format.) + ReleaseDate string + + // ConceptDOI is the DOI for all versions. + ConceptDOI string + + // DOI for the most recent release. + DOI string + + // ZenodoID is the Zenodo deposit ID for the most recent release. + ZenodoID string +} + +// Meta defines specific properties for the current version of this software. +var Meta = &Properties{ + Name: "addchain", + FullName: "mmcloughlin/addchain", + Description: "Cryptographic Addition Chain Generation in Go", + BuildVersion: buildversion, + ReleaseVersion: releaseversion, + ReleaseDate: releasedate, + ConceptDOI: conceptdoi, + DOI: doi, + ZenodoID: zenodoid, +} + +// Title is a full project title, suitable for a citation. +func (p *Properties) Title() string { + return fmt.Sprintf("%s: %s", p.Name, p.Description) +} + +// IsRelease reports whether the built version is a release. +func (p *Properties) IsRelease() bool { + return p.BuildVersion == p.ReleaseVersion +} + +// ReleaseTag returns the release tag corresponding to the most recent release. +func (p *Properties) ReleaseTag() string { + return VersionTagPrefix + p.ReleaseVersion +} + +// Module returns the Go module path. +func (p *Properties) Module() string { + return path.Join("github.com", p.FullName) +} + +// RepositoryURL returns a URL to the hosted repository. +func (p *Properties) RepositoryURL() string { + return "https://" + p.Module() +} + +// ReleaseURL returns the URL to the release page. +func (p *Properties) ReleaseURL() string { + return fmt.Sprintf("%s/releases/tag/%s", p.RepositoryURL(), p.ReleaseTag()) +} + +// ReleaseTime returns the release date as a time object. +func (p *Properties) ReleaseTime() (time.Time, error) { + return time.Parse("2006-01-02", p.ReleaseDate) +} + +// DOIURL returns the DOI URL corresponding to the most recent release. +func (p *Properties) DOIURL() string { return doiurl(p.DOI) } + +// ConceptDOIURL returns the DOI URL corresponding to the most recent release. +func (p *Properties) ConceptDOIURL() string { return doiurl(p.ConceptDOI) } + +func doiurl(doi string) string { + return "https://doi.org/" + doi +} diff --git a/vendor/github.com/mmcloughlin/addchain/meta/vars.go b/vendor/github.com/mmcloughlin/addchain/meta/vars.go new file mode 100644 index 00000000000..a034e8cd06c --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/meta/vars.go @@ -0,0 +1,10 @@ +package meta + +var ( + buildversion = "" + releaseversion = "0.4.0" + releasedate = "2021-10-30" + conceptdoi = "10.5281/zenodo.4625263" + doi = "10.5281/zenodo.5622943" + zenodoid = "5622943" +) diff --git a/vendor/github.com/mmcloughlin/addchain/program.go b/vendor/github.com/mmcloughlin/addchain/program.go new file mode 100644 index 00000000000..6449910b068 --- /dev/null +++ b/vendor/github.com/mmcloughlin/addchain/program.go @@ -0,0 +1,133 @@ +package addchain + +import ( + "fmt" + "math/big" + + "github.com/mmcloughlin/addchain/internal/bigint" +) + +// Op is an instruction to add positions I and J in a chain. +type Op struct{ I, J int } + +// IsDouble returns whether this operation is a doubling. +func (o Op) IsDouble() bool { return o.I == o.J } + +// Operands returns the indicies used in this operation. This will contain one +// or two entries depending on whether this is a doubling. +func (o Op) Operands() []int { + if o.IsDouble() { + return []int{o.I} + } + return []int{o.I, o.J} +} + +// Uses reports whether the given index is one of the operands. +func (o Op) Uses(i int) bool { + return o.I == i || o.J == i +} + +// Program is a sequence of operations. +type Program []Op + +// Shift appends a sequence of operations that bitwise shift index i left by s, +// equivalent to s double operations. Returns the index of the result. +func (p *Program) Shift(i int, s uint) (int, error) { + for ; s > 0; s-- { + next, err := p.Double(i) + if err != nil { + return 0, err + } + i = next + } + return i, nil +} + +// Double appends an operation that doubles index i. Returns the index of the +// result. +func (p *Program) Double(i int) (int, error) { + return p.Add(i, i) +} + +// Add appends an operation that adds indices i and j. Returns the index of the +// result. +func (p *Program) Add(i, j int) (int, error) { + if err := p.boundscheck(i); err != nil { + return 0, err + } + if err := p.boundscheck(j); err != nil { + return 0, err + } + *p = append(*p, Op{i, j}) + return len(*p), nil +} + +// boundscheck returns an error if i is out of bounds. +func (p Program) boundscheck(i int) error { + // Note the corresponding chain is one longer than the program. + n := len(p) + switch { + case i < 0: + return fmt.Errorf("negative index %d", i) + case i > n: + return fmt.Errorf("index %d out of bounds", i) + } + return nil +} + +// Doubles returns the number of doubles in the program. +func (p Program) Doubles() int { + doubles, _ := p.Count() + return doubles +} + +// Adds returns the number of adds in the program. +func (p Program) Adds() int { + _, adds := p.Count() + return adds +} + +// Count returns the number of doubles and adds in the program. +func (p Program) Count() (doubles, adds int) { + for _, op := range p { + if op.IsDouble() { + doubles++ + } else { + adds++ + } + } + return +} + +// Evaluate executes the program and returns the resulting chain. +func (p Program) Evaluate() Chain { + c := New() + for _, op := range p { + sum := new(big.Int).Add(c[op.I], c[op.J]) + c = append(c, sum) + } + return c +} + +// ReadCounts returns how many times each index is read in the program. +func (p Program) ReadCounts() []int { + reads := make([]int, len(p)+1) + for _, op := range p { + for _, i := range op.Operands() { + reads[i]++ + } + } + return reads +} + +// Dependencies returns an array of bitsets where each bitset contains the set +// of indicies that contributed to that position. +func (p Program) Dependencies() []*big.Int { + bitsets := []*big.Int{bigint.One()} + for i, op := range p { + bitset := new(big.Int).Or(bitsets[op.I], bitsets[op.J]) + bitset.SetBit(bitset, i+1, 1) + bitsets = append(bitsets, bitset) + } + return bitsets +} diff --git a/vendor/golang.org/x/sys/unix/ioctl_linux.go b/vendor/golang.org/x/sys/unix/ioctl_linux.go new file mode 100644 index 00000000000..48773f730ac --- /dev/null +++ b/vendor/golang.org/x/sys/unix/ioctl_linux.go @@ -0,0 +1,196 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "runtime" + "unsafe" +) + +// IoctlRetInt performs an ioctl operation specified by req on a device +// associated with opened file descriptor fd, and returns a non-negative +// integer that is returned by the ioctl syscall. +func IoctlRetInt(fd int, req uint) (int, error) { + ret, _, err := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), 0) + if err != 0 { + return 0, err + } + return int(ret), nil +} + +func IoctlGetUint32(fd int, req uint) (uint32, error) { + var value uint32 + err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) + return value, err +} + +func IoctlGetRTCTime(fd int) (*RTCTime, error) { + var value RTCTime + err := ioctl(fd, RTC_RD_TIME, uintptr(unsafe.Pointer(&value))) + return &value, err +} + +func IoctlSetRTCTime(fd int, value *RTCTime) error { + err := ioctl(fd, RTC_SET_TIME, uintptr(unsafe.Pointer(value))) + runtime.KeepAlive(value) + return err +} + +func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) { + var value RTCWkAlrm + err := ioctl(fd, RTC_WKALM_RD, uintptr(unsafe.Pointer(&value))) + return &value, err +} + +func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error { + err := ioctl(fd, RTC_WKALM_SET, uintptr(unsafe.Pointer(value))) + runtime.KeepAlive(value) + return err +} + +type ifreqEthtool struct { + name [IFNAMSIZ]byte + data unsafe.Pointer +} + +// IoctlGetEthtoolDrvinfo fetches ethtool driver information for the network +// device specified by ifname. +func IoctlGetEthtoolDrvinfo(fd int, ifname string) (*EthtoolDrvinfo, error) { + // Leave room for terminating NULL byte. + if len(ifname) >= IFNAMSIZ { + return nil, EINVAL + } + + value := EthtoolDrvinfo{ + Cmd: ETHTOOL_GDRVINFO, + } + ifreq := ifreqEthtool{ + data: unsafe.Pointer(&value), + } + copy(ifreq.name[:], ifname) + err := ioctl(fd, SIOCETHTOOL, uintptr(unsafe.Pointer(&ifreq))) + runtime.KeepAlive(ifreq) + return &value, err +} + +// IoctlGetWatchdogInfo fetches information about a watchdog device from the +// Linux watchdog API. For more information, see: +// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. +func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) { + var value WatchdogInfo + err := ioctl(fd, WDIOC_GETSUPPORT, uintptr(unsafe.Pointer(&value))) + return &value, err +} + +// IoctlWatchdogKeepalive issues a keepalive ioctl to a watchdog device. For +// more information, see: +// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. +func IoctlWatchdogKeepalive(fd int) error { + return ioctl(fd, WDIOC_KEEPALIVE, 0) +} + +// IoctlFileCloneRange performs an FICLONERANGE ioctl operation to clone the +// range of data conveyed in value to the file associated with the file +// descriptor destFd. See the ioctl_ficlonerange(2) man page for details. +func IoctlFileCloneRange(destFd int, value *FileCloneRange) error { + err := ioctl(destFd, FICLONERANGE, uintptr(unsafe.Pointer(value))) + runtime.KeepAlive(value) + return err +} + +// IoctlFileClone performs an FICLONE ioctl operation to clone the entire file +// associated with the file description srcFd to the file associated with the +// file descriptor destFd. See the ioctl_ficlone(2) man page for details. +func IoctlFileClone(destFd, srcFd int) error { + return ioctl(destFd, FICLONE, uintptr(srcFd)) +} + +type FileDedupeRange struct { + Src_offset uint64 + Src_length uint64 + Reserved1 uint16 + Reserved2 uint32 + Info []FileDedupeRangeInfo +} + +type FileDedupeRangeInfo struct { + Dest_fd int64 + Dest_offset uint64 + Bytes_deduped uint64 + Status int32 + Reserved uint32 +} + +// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the +// range of data conveyed in value from the file associated with the file +// descriptor srcFd to the value.Info destinations. See the +// ioctl_fideduperange(2) man page for details. +func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error { + buf := make([]byte, SizeofRawFileDedupeRange+ + len(value.Info)*SizeofRawFileDedupeRangeInfo) + rawrange := (*RawFileDedupeRange)(unsafe.Pointer(&buf[0])) + rawrange.Src_offset = value.Src_offset + rawrange.Src_length = value.Src_length + rawrange.Dest_count = uint16(len(value.Info)) + rawrange.Reserved1 = value.Reserved1 + rawrange.Reserved2 = value.Reserved2 + + for i := range value.Info { + rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( + uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + + uintptr(i*SizeofRawFileDedupeRangeInfo))) + rawinfo.Dest_fd = value.Info[i].Dest_fd + rawinfo.Dest_offset = value.Info[i].Dest_offset + rawinfo.Bytes_deduped = value.Info[i].Bytes_deduped + rawinfo.Status = value.Info[i].Status + rawinfo.Reserved = value.Info[i].Reserved + } + + err := ioctl(srcFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(&buf[0]))) + + // Output + for i := range value.Info { + rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( + uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + + uintptr(i*SizeofRawFileDedupeRangeInfo))) + value.Info[i].Dest_fd = rawinfo.Dest_fd + value.Info[i].Dest_offset = rawinfo.Dest_offset + value.Info[i].Bytes_deduped = rawinfo.Bytes_deduped + value.Info[i].Status = rawinfo.Status + value.Info[i].Reserved = rawinfo.Reserved + } + + return err +} + +func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error { + err := ioctl(fd, HIDIOCGRDESC, uintptr(unsafe.Pointer(value))) + runtime.KeepAlive(value) + return err +} + +func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) { + var value HIDRawDevInfo + err := ioctl(fd, HIDIOCGRAWINFO, uintptr(unsafe.Pointer(&value))) + return &value, err +} + +func IoctlHIDGetRawName(fd int) (string, error) { + var value [_HIDIOCGRAWNAME_LEN]byte + err := ioctl(fd, _HIDIOCGRAWNAME, uintptr(unsafe.Pointer(&value[0]))) + return ByteSliceToString(value[:]), err +} + +func IoctlHIDGetRawPhys(fd int) (string, error) { + var value [_HIDIOCGRAWPHYS_LEN]byte + err := ioctl(fd, _HIDIOCGRAWPHYS, uintptr(unsafe.Pointer(&value[0]))) + return ByteSliceToString(value[:]), err +} + +func IoctlHIDGetRawUniq(fd int) (string, error) { + var value [_HIDIOCGRAWUNIQ_LEN]byte + err := ioctl(fd, _HIDIOCGRAWUNIQ, uintptr(unsafe.Pointer(&value[0]))) + return ByteSliceToString(value[:]), err +} diff --git a/vendor/golang.org/x/sys/unix/mkerrors.sh b/vendor/golang.org/x/sys/unix/mkerrors.sh index f2bc8631494..007358af8fc 100644 --- a/vendor/golang.org/x/sys/unix/mkerrors.sh +++ b/vendor/golang.org/x/sys/unix/mkerrors.sh @@ -405,10 +405,11 @@ includes_SunOS=' #include #include #include +#include #include -#include #include #include +#include ' @@ -499,10 +500,10 @@ ccflags="$@" $2 ~ /^LOCK_(SH|EX|NB|UN)$/ || $2 ~ /^LO_(KEY|NAME)_SIZE$/ || $2 ~ /^LOOP_(CLR|CTL|GET|SET)_/ || - $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL)_/ || + $2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|TCP|MCAST|EVFILT|NOTE|SHUT|PROT|MAP|MFD|T?PACKET|MSG|SCM|MCL|DT|MADV|PR|LOCAL)_/ || $2 ~ /^TP_STATUS_/ || $2 ~ /^FALLOC_/ || - $2 ~ /^ICMP(V6)?_FILTER/ || + $2 ~ /^ICMPV?6?_(FILTER|SEC)/ || $2 == "SOMAXCONN" || $2 == "NAME_MAX" || $2 == "IFNAMSIZ" || diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go index 44ea96e39c6..4263953bee3 100644 --- a/vendor/golang.org/x/sys/unix/syscall_linux.go +++ b/vendor/golang.org/x/sys/unix/syscall_linux.go @@ -70,167 +70,7 @@ func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { // ioctl itself should not be exposed directly, but additional get/set // functions for specific types are permissible. - -// IoctlRetInt performs an ioctl operation specified by req on a device -// associated with opened file descriptor fd, and returns a non-negative -// integer that is returned by the ioctl syscall. -func IoctlRetInt(fd int, req uint) (int, error) { - ret, _, err := Syscall(SYS_IOCTL, uintptr(fd), uintptr(req), 0) - if err != 0 { - return 0, err - } - return int(ret), nil -} - -func IoctlSetRTCTime(fd int, value *RTCTime) error { - err := ioctl(fd, RTC_SET_TIME, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err -} - -func IoctlSetRTCWkAlrm(fd int, value *RTCWkAlrm) error { - err := ioctl(fd, RTC_WKALM_SET, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err -} - -func IoctlGetUint32(fd int, req uint) (uint32, error) { - var value uint32 - err := ioctl(fd, req, uintptr(unsafe.Pointer(&value))) - return value, err -} - -func IoctlGetRTCTime(fd int) (*RTCTime, error) { - var value RTCTime - err := ioctl(fd, RTC_RD_TIME, uintptr(unsafe.Pointer(&value))) - return &value, err -} - -// IoctlGetWatchdogInfo fetches information about a watchdog device from the -// Linux watchdog API. For more information, see: -// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. -func IoctlGetWatchdogInfo(fd int) (*WatchdogInfo, error) { - var value WatchdogInfo - err := ioctl(fd, WDIOC_GETSUPPORT, uintptr(unsafe.Pointer(&value))) - return &value, err -} - -func IoctlGetRTCWkAlrm(fd int) (*RTCWkAlrm, error) { - var value RTCWkAlrm - err := ioctl(fd, RTC_WKALM_RD, uintptr(unsafe.Pointer(&value))) - return &value, err -} - -// IoctlFileCloneRange performs an FICLONERANGE ioctl operation to clone the -// range of data conveyed in value to the file associated with the file -// descriptor destFd. See the ioctl_ficlonerange(2) man page for details. -func IoctlFileCloneRange(destFd int, value *FileCloneRange) error { - err := ioctl(destFd, FICLONERANGE, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err -} - -// IoctlFileClone performs an FICLONE ioctl operation to clone the entire file -// associated with the file description srcFd to the file associated with the -// file descriptor destFd. See the ioctl_ficlone(2) man page for details. -func IoctlFileClone(destFd, srcFd int) error { - return ioctl(destFd, FICLONE, uintptr(srcFd)) -} - -type FileDedupeRange struct { - Src_offset uint64 - Src_length uint64 - Reserved1 uint16 - Reserved2 uint32 - Info []FileDedupeRangeInfo -} - -type FileDedupeRangeInfo struct { - Dest_fd int64 - Dest_offset uint64 - Bytes_deduped uint64 - Status int32 - Reserved uint32 -} - -// IoctlFileDedupeRange performs an FIDEDUPERANGE ioctl operation to share the -// range of data conveyed in value from the file associated with the file -// descriptor srcFd to the value.Info destinations. See the -// ioctl_fideduperange(2) man page for details. -func IoctlFileDedupeRange(srcFd int, value *FileDedupeRange) error { - buf := make([]byte, SizeofRawFileDedupeRange+ - len(value.Info)*SizeofRawFileDedupeRangeInfo) - rawrange := (*RawFileDedupeRange)(unsafe.Pointer(&buf[0])) - rawrange.Src_offset = value.Src_offset - rawrange.Src_length = value.Src_length - rawrange.Dest_count = uint16(len(value.Info)) - rawrange.Reserved1 = value.Reserved1 - rawrange.Reserved2 = value.Reserved2 - - for i := range value.Info { - rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( - uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + - uintptr(i*SizeofRawFileDedupeRangeInfo))) - rawinfo.Dest_fd = value.Info[i].Dest_fd - rawinfo.Dest_offset = value.Info[i].Dest_offset - rawinfo.Bytes_deduped = value.Info[i].Bytes_deduped - rawinfo.Status = value.Info[i].Status - rawinfo.Reserved = value.Info[i].Reserved - } - - err := ioctl(srcFd, FIDEDUPERANGE, uintptr(unsafe.Pointer(&buf[0]))) - - // Output - for i := range value.Info { - rawinfo := (*RawFileDedupeRangeInfo)(unsafe.Pointer( - uintptr(unsafe.Pointer(&buf[0])) + uintptr(SizeofRawFileDedupeRange) + - uintptr(i*SizeofRawFileDedupeRangeInfo))) - value.Info[i].Dest_fd = rawinfo.Dest_fd - value.Info[i].Dest_offset = rawinfo.Dest_offset - value.Info[i].Bytes_deduped = rawinfo.Bytes_deduped - value.Info[i].Status = rawinfo.Status - value.Info[i].Reserved = rawinfo.Reserved - } - - return err -} - -// IoctlWatchdogKeepalive issues a keepalive ioctl to a watchdog device. For -// more information, see: -// https://www.kernel.org/doc/html/latest/watchdog/watchdog-api.html. -func IoctlWatchdogKeepalive(fd int) error { - return ioctl(fd, WDIOC_KEEPALIVE, 0) -} - -func IoctlHIDGetDesc(fd int, value *HIDRawReportDescriptor) error { - err := ioctl(fd, HIDIOCGRDESC, uintptr(unsafe.Pointer(value))) - runtime.KeepAlive(value) - return err -} - -func IoctlHIDGetRawInfo(fd int) (*HIDRawDevInfo, error) { - var value HIDRawDevInfo - err := ioctl(fd, HIDIOCGRAWINFO, uintptr(unsafe.Pointer(&value))) - return &value, err -} - -func IoctlHIDGetRawName(fd int) (string, error) { - var value [_HIDIOCGRAWNAME_LEN]byte - err := ioctl(fd, _HIDIOCGRAWNAME, uintptr(unsafe.Pointer(&value[0]))) - return ByteSliceToString(value[:]), err -} - -func IoctlHIDGetRawPhys(fd int) (string, error) { - var value [_HIDIOCGRAWPHYS_LEN]byte - err := ioctl(fd, _HIDIOCGRAWPHYS, uintptr(unsafe.Pointer(&value[0]))) - return ByteSliceToString(value[:]), err -} - -func IoctlHIDGetRawUniq(fd int) (string, error) { - var value [_HIDIOCGRAWUNIQ_LEN]byte - err := ioctl(fd, _HIDIOCGRAWUNIQ, uintptr(unsafe.Pointer(&value[0]))) - return ByteSliceToString(value[:]), err -} +// These are defined in ioctl.go and ioctl_linux.go. //sys Linkat(olddirfd int, oldpath string, newdirfd int, newpath string, flags int) (err error) @@ -857,16 +697,19 @@ type SockaddrVM struct { // CID and Port specify a context ID and port address for a VM socket. // Guests have a unique CID, and hosts may have a well-known CID of: // - VMADDR_CID_HYPERVISOR: refers to the hypervisor process. + // - VMADDR_CID_LOCAL: refers to local communication (loopback). // - VMADDR_CID_HOST: refers to other processes on the host. - CID uint32 - Port uint32 - raw RawSockaddrVM + CID uint32 + Port uint32 + Flags uint8 + raw RawSockaddrVM } func (sa *SockaddrVM) sockaddr() (unsafe.Pointer, _Socklen, error) { sa.raw.Family = AF_VSOCK sa.raw.Port = sa.Port sa.raw.Cid = sa.CID + sa.raw.Flags = sa.Flags return unsafe.Pointer(&sa.raw), SizeofSockaddrVM, nil } @@ -1171,8 +1014,9 @@ func anyToSockaddr(fd int, rsa *RawSockaddrAny) (Sockaddr, error) { case AF_VSOCK: pp := (*RawSockaddrVM)(unsafe.Pointer(rsa)) sa := &SockaddrVM{ - CID: pp.Cid, - Port: pp.Port, + CID: pp.Cid, + Port: pp.Port, + Flags: pp.Flags, } return sa, nil case AF_BLUETOOTH: diff --git a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go index 0326a6b3af9..3df99f285f1 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go +++ b/vendor/golang.org/x/sys/unix/zerrors_freebsd_arm.go @@ -1022,6 +1022,15 @@ const ( MAP_RESERVED0100 = 0x100 MAP_SHARED = 0x1 MAP_STACK = 0x400 + MCAST_BLOCK_SOURCE = 0x54 + MCAST_EXCLUDE = 0x2 + MCAST_INCLUDE = 0x1 + MCAST_JOIN_GROUP = 0x50 + MCAST_JOIN_SOURCE_GROUP = 0x52 + MCAST_LEAVE_GROUP = 0x51 + MCAST_LEAVE_SOURCE_GROUP = 0x53 + MCAST_UNBLOCK_SOURCE = 0x55 + MCAST_UNDEFINED = 0x0 MCL_CURRENT = 0x1 MCL_FUTURE = 0x2 MNT_ACLS = 0x8000000 diff --git a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go index 65fb2c5cd83..1afee6a0890 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go +++ b/vendor/golang.org/x/sys/unix/zerrors_solaris_amd64.go @@ -366,6 +366,7 @@ const ( HUPCL = 0x400 IBSHIFT = 0x10 ICANON = 0x2 + ICMP6_FILTER = 0x1 ICRNL = 0x100 IEXTEN = 0x8000 IFF_ADDRCONF = 0x80000 @@ -612,6 +613,7 @@ const ( IP_RECVPKTINFO = 0x1a IP_RECVRETOPTS = 0x6 IP_RECVSLLA = 0xa + IP_RECVTOS = 0xc IP_RECVTTL = 0xb IP_RETOPTS = 0x8 IP_REUSEADDR = 0x104 @@ -704,6 +706,7 @@ const ( O_APPEND = 0x8 O_CLOEXEC = 0x800000 O_CREAT = 0x100 + O_DIRECT = 0x2000000 O_DIRECTORY = 0x1000000 O_DSYNC = 0x40 O_EXCL = 0x400 diff --git a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go index 4117ce08a50..4e87b4bebd5 100644 --- a/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go +++ b/vendor/golang.org/x/sys/unix/zerrors_zos_s390x.go @@ -137,6 +137,7 @@ const ( IP_TTL = 3 IP_UNBLOCK_SOURCE = 11 ICANON = 0x0010 + ICMP6_FILTER = 0x26 ICRNL = 0x0002 IEXTEN = 0x0020 IGNBRK = 0x0004 @@ -163,6 +164,12 @@ const ( MAP_PRIVATE = 0x1 // changes are private MAP_SHARED = 0x2 // changes are shared MAP_FIXED = 0x4 // place exactly + MCAST_JOIN_GROUP = 40 + MCAST_LEAVE_GROUP = 41 + MCAST_JOIN_SOURCE_GROUP = 42 + MCAST_LEAVE_SOURCE_GROUP = 43 + MCAST_BLOCK_SOURCE = 44 + MCAST_UNBLOCK_SOURCE = 45 MS_SYNC = 0x1 // msync - synchronous writes MS_ASYNC = 0x2 // asynchronous writes MS_INVALIDATE = 0x4 // invalidate mappings diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index c769e73cd6f..3bfc6f7323b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -3698,6 +3698,21 @@ const ( ETHTOOL_A_TUNNEL_INFO_MAX = 0x2 ) +type EthtoolDrvinfo struct { + Cmd uint32 + Driver [32]byte + Version [32]byte + Fw_version [32]byte + Bus_info [32]byte + Erom_version [32]byte + Reserved2 [12]byte + N_priv_flags uint32 + N_stats uint32 + Testinfo_len uint32 + Eedump_len uint32 + Regdump_len uint32 +} + type ( HIDRawReportDescriptor struct { Size uint32 diff --git a/vendor/golang.org/x/sys/windows/security_windows.go b/vendor/golang.org/x/sys/windows/security_windows.go index 0e428ecbbde..111c10d3a7f 100644 --- a/vendor/golang.org/x/sys/windows/security_windows.go +++ b/vendor/golang.org/x/sys/windows/security_windows.go @@ -1334,7 +1334,11 @@ func (absoluteSD *SECURITY_DESCRIPTOR) ToSelfRelative() (selfRelativeSD *SECURIT } func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() *SECURITY_DESCRIPTOR { - sdLen := (int)(selfRelativeSD.Length()) + sdLen := int(selfRelativeSD.Length()) + const min = int(unsafe.Sizeof(SECURITY_DESCRIPTOR{})) + if sdLen < min { + sdLen = min + } var src []byte h := (*unsafeheader.Slice)(unsafe.Pointer(&src)) @@ -1342,7 +1346,15 @@ func (selfRelativeSD *SECURITY_DESCRIPTOR) copySelfRelativeSecurityDescriptor() h.Len = sdLen h.Cap = sdLen - dst := make([]byte, sdLen) + const psize = int(unsafe.Sizeof(uintptr(0))) + + var dst []byte + h = (*unsafeheader.Slice)(unsafe.Pointer(&dst)) + alloc := make([]uintptr, (sdLen+psize-1)/psize) + h.Data = (*unsafeheader.Slice)(unsafe.Pointer(&alloc)).Data + h.Len = sdLen + h.Cap = sdLen + copy(dst, src) return (*SECURITY_DESCRIPTOR)(unsafe.Pointer(&dst[0])) } diff --git a/vendor/modules.txt b/vendor/modules.txt index 9873fade591..d204e4f5e68 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -8,7 +8,7 @@ github.com/Azure/go-ansiterm/winterm # github.com/DataDog/zstd v1.4.5 ## explicit github.com/DataDog/zstd -# github.com/IBM/idemix v0.0.0-20210930104432-e4a1410f5353 +# github.com/IBM/idemix v0.0.0-20220112103229-701e7610d405 ## explicit github.com/IBM/idemix github.com/IBM/idemix/bccsp @@ -20,7 +20,7 @@ github.com/IBM/idemix/bccsp/schemes/dlog/crypto/translator/amcl github.com/IBM/idemix/bccsp/schemes/dlog/handlers github.com/IBM/idemix/common/flogging github.com/IBM/idemix/common/flogging/fabenc -# github.com/IBM/mathlib v0.0.0-20210928081244-f5486459a290 +# github.com/IBM/mathlib v0.0.0-20220112091634-0a7378db6912 github.com/IBM/mathlib github.com/IBM/mathlib/driver github.com/IBM/mathlib/driver/amcl @@ -71,14 +71,18 @@ github.com/alecthomas/template/parse github.com/alecthomas/units # github.com/beorn7/perks v1.0.1 github.com/beorn7/perks/quantile -# github.com/cespare/xxhash/v2 v2.1.1 +# github.com/cespare/xxhash/v2 v2.1.1 => github.com/cespare/xxhash/v2 v2.1.2 github.com/cespare/xxhash/v2 -# github.com/consensys/gnark-crypto v0.4.0 +# github.com/consensys/gnark-crypto v0.6.0 github.com/consensys/gnark-crypto/ecc github.com/consensys/gnark-crypto/ecc/bn254 github.com/consensys/gnark-crypto/ecc/bn254/fp github.com/consensys/gnark-crypto/ecc/bn254/fr +github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc github.com/consensys/gnark-crypto/ecc/bn254/internal/fptower +github.com/consensys/gnark-crypto/field +github.com/consensys/gnark-crypto/field/internal/addchain +github.com/consensys/gnark-crypto/internal/generator/config github.com/consensys/gnark-crypto/internal/parallel # github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59 github.com/containerd/cgroups/stats/v1 @@ -261,6 +265,29 @@ github.com/miekg/pkcs11 # github.com/mitchellh/mapstructure v1.3.2 ## explicit github.com/mitchellh/mapstructure +# github.com/mmcloughlin/addchain v0.4.0 +github.com/mmcloughlin/addchain +github.com/mmcloughlin/addchain/acc +github.com/mmcloughlin/addchain/acc/ast +github.com/mmcloughlin/addchain/acc/ir +github.com/mmcloughlin/addchain/acc/parse +github.com/mmcloughlin/addchain/acc/parse/internal/parser +github.com/mmcloughlin/addchain/acc/pass +github.com/mmcloughlin/addchain/acc/printer +github.com/mmcloughlin/addchain/alg +github.com/mmcloughlin/addchain/alg/contfrac +github.com/mmcloughlin/addchain/alg/dict +github.com/mmcloughlin/addchain/alg/ensemble +github.com/mmcloughlin/addchain/alg/exec +github.com/mmcloughlin/addchain/alg/heuristic +github.com/mmcloughlin/addchain/alg/opt +github.com/mmcloughlin/addchain/internal/bigint +github.com/mmcloughlin/addchain/internal/bigints +github.com/mmcloughlin/addchain/internal/bigvector +github.com/mmcloughlin/addchain/internal/container/heap +github.com/mmcloughlin/addchain/internal/errutil +github.com/mmcloughlin/addchain/internal/print +github.com/mmcloughlin/addchain/meta # github.com/moby/sys/mount v0.2.0 github.com/moby/sys/mount # github.com/moby/sys/mountinfo v0.4.0 @@ -456,7 +483,7 @@ golang.org/x/net/internal/timeseries golang.org/x/net/trace # golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e golang.org/x/sync/errgroup -# golang.org/x/sys v0.0.0-20210326220804-49726bf1d181 +# golang.org/x/sys v0.0.0-20210420205809-ac73e9fd8988 golang.org/x/sys/cpu golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix @@ -557,3 +584,4 @@ gopkg.in/yaml.v2 ## explicit gopkg.in/yaml.v3 # github.com/onsi/gomega => github.com/onsi/gomega v1.9.0 +# github.com/cespare/xxhash/v2 => github.com/cespare/xxhash/v2 v2.1.2