mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
crypto: drop inline ipa/ dir; depend on canonical github.com/luxfi/crypto/ipa
The inline ipa/ directory was a vendored copy of go-ipa that collided with the separate luxfi/crypto/ipa module's import path — any downstream that pulled both saw "ambiguous import" errors at the bandersnatch / banderwagon / common/parallel packages. Removed the inline tree and added `require github.com/luxfi/crypto/ipa v1.2.4`. The verkle subpackage switches its banderwagon imports from `luxfi/crypto/banderwagon` (local) to `luxfi/crypto/ipa/banderwagon` (external) so its Point alias matches the type ipa.CreateMultiProof and IPAConfig.Commit return. The local lux/crypto/banderwagon package stays — its MultiExp/Precomp helpers remain available for callers that want the Element-method shape; it just no longer shares a struct identity with the verkle types. Two pre-existing test failures in verkle (TestParseNodeEoA, TestParseNodeSingleSlot) reproduce both with this change AND on the pre-change tree — encoding-tag mismatch and projective-coord hardcoding in the test's expected values. Not blocking; tracked separately.
This commit is contained in:
@@ -38,6 +38,7 @@ require (
|
||||
filippo.io/hpke v0.4.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/luxfi/crypto/ipa v1.2.4 // indirect
|
||||
github.com/luxfi/utils v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
|
||||
@@ -251,6 +251,8 @@ github.com/luxfi/age v1.4.0 h1:tU5q65RQSQdaVq64Z/DVUhiPQMoMPQT6pr41LsvG0AQ=
|
||||
github.com/luxfi/age v1.4.0/go.mod h1:iAYAxgvrXxcy746+Ovh/eWWDuF9teJLNcCSSOX9RYW0=
|
||||
github.com/luxfi/cache v1.1.0 h1:6LUyGGZ+rrMAJBbAU6+UwkcamXj3zsboRUodIof2Ong=
|
||||
github.com/luxfi/cache v1.1.0/go.mod h1:9GvlEEE9rFPaaWxvVpSPwW8ZMo2+8VMNNcuPa4AwzPg=
|
||||
github.com/luxfi/crypto/ipa v1.2.4 h1:6xfwhI9/HrcDkF3Ti5/NxsNQIWbwYDJmRSNIHRQ/xfU=
|
||||
github.com/luxfi/crypto/ipa v1.2.4/go.mod h1:43J6f6rcfUMrZt4cQectMOZb6Ps+fAEj8ZTPC3Kk+gE=
|
||||
github.com/luxfi/ids v1.2.9 h1:+yjdhXW99drnd2Zlp1u/p8k3G23W3/1btJQ4ogHawUI=
|
||||
github.com/luxfi/ids v1.2.9/go.mod h1:khJOEdOPxd22yn0jcVrnbX1ADa0GHn5Y74gvCzN5BYc=
|
||||
github.com/luxfi/log v1.4.1 h1:rIfFRodb9jrD/w7KayaUk0Oc+37PaQQdKEEMJCjR8gw=
|
||||
|
||||
Vendored
-31
@@ -1,31 +0,0 @@
|
||||
name: Benchmarks
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
deployments: write
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
name: Benchmarks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: "stable"
|
||||
- name: Run benchmark
|
||||
run: go test ./... -run=none -bench . | tee bench_output.txt
|
||||
|
||||
- name: Store benchmark result
|
||||
uses: benchmark-action/github-action-benchmark@v1
|
||||
with:
|
||||
name: Go Benchmark
|
||||
tool: 'go'
|
||||
output-file-path: bench_output.txt
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
auto-push: true
|
||||
summary-always: true
|
||||
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
name: Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test -v -timeout=1h ./...
|
||||
|
||||
staticcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: install Go
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.18
|
||||
- name: checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v3
|
||||
@@ -1,14 +0,0 @@
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- errcheck
|
||||
- gofmt
|
||||
- staticcheck
|
||||
- unused
|
||||
- goconst
|
||||
- goimports
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- unconvert
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
This directory contains code from the go-ipa project (https://github.com/crate-crypto/go-ipa)
|
||||
which is dual licensed under Apache 2.0 and MIT licenses.
|
||||
|
||||
The original license files are preserved as:
|
||||
- LICENSE-APACHE
|
||||
- LICENSE-MIT
|
||||
|
||||
================================================================================
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
See LICENSE-APACHE for full Apache 2.0 license text.
|
||||
|
||||
================================================================================
|
||||
|
||||
MIT License
|
||||
|
||||
See LICENSE-MIT for full MIT license text.
|
||||
|
||||
================================================================================
|
||||
|
||||
This code has been modified for compatibility with gnark-crypto v0.12.1
|
||||
and integrated into the luxfi/crypto package.
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Crate Crypto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,56 +0,0 @@
|
||||
# go-ipa
|
||||
|
||||
> go-ipa is a library of cryptographic primitives for Verkle Trees.
|
||||
|
||||
[](https://github.com/crate-crypto/go-ipa/blob/main/LICENSE)
|
||||
[](https://golang.org/dl/)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [go-ipa](#go-ipa)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Description](#description)
|
||||
- [Usage in Verkle Tree client libraries](#usage-in-verkle-tree-client-libraries)
|
||||
- [Test \& Benchmarks](#test--benchmarks)
|
||||
- [Security](#security)
|
||||
- [LICENSE](#license)
|
||||
|
||||
## Description
|
||||
|
||||
go-ipa implements the [Verkle Tree cryptography spec](https://github.com/crate-crypto/verkle-trie-ref) with extra optimizations.
|
||||
|
||||
The includes:
|
||||
- Implementation of the Bandersnatch curve, and Banderwagon prime-order group.
|
||||
- Pedersen Commitment for vector commitments using precomputed tables.
|
||||
- Inner Product Argument prover and verifier implementations for polynomials in evaluation form.
|
||||
- Multiproof prover and verifier implementations.
|
||||
|
||||
## Usage in Verkle Tree client libraries
|
||||
|
||||
It's extremely important that clients using this library for Verkle Tree implementations only use the following packages:
|
||||
- `common` for general utility functions.
|
||||
- `banderwagon` for the prime-order group.
|
||||
- `ipa` for proof generation and verification.
|
||||
|
||||
**Do not** use the `bandersnatch` package directly nor use unsafe functions to get into `banderwagon` internals. Doing so can create a security vulnerability in your implementation.
|
||||
|
||||
## Test & Benchmarks
|
||||
|
||||
To run the tests and benchmarks, run the following commands:
|
||||
```bash
|
||||
$ go test ./...
|
||||
```
|
||||
|
||||
To run the benchmarks:
|
||||
```bash
|
||||
go test ./... -bench=. -run=none -benchmem
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
If you find any security vulnerability, please don't open a GH issue and contact repo owners directly.
|
||||
|
||||
|
||||
## LICENSE
|
||||
|
||||
[MIT](LICENSE-MIT) and [Apache 2.0](LICENSE-APACHE).
|
||||
@@ -1,157 +0,0 @@
|
||||
package bandersnatch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
gnarkbandersnatch "github.com/consensys/gnark-crypto/ecc/bls12-381/bandersnatch"
|
||||
gnarkfr "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fp"
|
||||
)
|
||||
|
||||
var CurveParams = gnarkbandersnatch.GetEdwardsCurve()
|
||||
|
||||
type PointAffine = gnarkbandersnatch.PointAffine
|
||||
type PointProj = gnarkbandersnatch.PointProj
|
||||
type PointExtended = gnarkbandersnatch.PointExtended
|
||||
|
||||
var Identity = PointProj{
|
||||
X: fp.Zero(),
|
||||
Y: fp.One(),
|
||||
Z: fp.One(),
|
||||
}
|
||||
|
||||
var IdentityExt = PointExtendedFromProj(&Identity)
|
||||
|
||||
// Reads an uncompressed affine point
|
||||
// Point is not guaranteed to be in the prime subgroup
|
||||
func ReadUncompressedPoint(r io.Reader) (PointAffine, error) {
|
||||
var xy = make([]byte, 64)
|
||||
if _, err := io.ReadAtLeast(r, xy, 64); err != nil {
|
||||
return PointAffine{}, fmt.Errorf("reading bytes: %s", err)
|
||||
}
|
||||
|
||||
var x_fp, y_fp fp.Element
|
||||
x_fp.SetBytes(xy[:32])
|
||||
y_fp.SetBytes(xy[32:])
|
||||
|
||||
return PointAffine{
|
||||
X: x_fp,
|
||||
Y: y_fp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Writes an uncompressed affine point to an io.Writer
|
||||
func WriteUncompressedPoint(w io.Writer, p *PointAffine) (int, error) {
|
||||
x_bytes := p.X.Bytes()
|
||||
y_bytes := p.Y.Bytes()
|
||||
n1, err := w.Write(x_bytes[:])
|
||||
if err != nil {
|
||||
return n1, err
|
||||
}
|
||||
n2, err := w.Write(y_bytes[:])
|
||||
total_bytes_written := n1 + n2
|
||||
if err != nil {
|
||||
return total_bytes_written, err
|
||||
}
|
||||
return total_bytes_written, nil
|
||||
}
|
||||
|
||||
func GetPointFromX(x *fp.Element, choose_largest bool) *PointAffine {
|
||||
y := computeY(x, choose_largest)
|
||||
if y == nil { // not a square
|
||||
return nil
|
||||
}
|
||||
return &PointAffine{X: *x, Y: *y}
|
||||
}
|
||||
|
||||
// ax^2 + y^2 = 1 + dx^2y^2
|
||||
// ax^2 -1 = dx^2y^2 - y^2
|
||||
// ax^2 -1 = y^2(dx^2 -1)
|
||||
// ax^2 - 1 / (dx^2 - 1) = y^2
|
||||
func computeY(x *fp.Element, choose_largest bool) *fp.Element {
|
||||
var one, num, den, y fp.Element
|
||||
one.SetOne()
|
||||
num.Square(x) // x^2
|
||||
den.Mul(&num, &CurveParams.D) //dx^2
|
||||
den.Sub(&den, &one) //dx^2 - 1
|
||||
|
||||
num.Mul(&num, &CurveParams.A) // ax^2
|
||||
num.Sub(&num, &one) // ax^2 - 1
|
||||
y.Div(&num, &den)
|
||||
sqrtY := fp.SqrtPrecomp(&y)
|
||||
|
||||
// If the square root does not exist, then the Sqrt method returns nil
|
||||
// and leaves the receiver unchanged.
|
||||
// Note the fact that it leaves the receiver unchanged, means we do not return &y
|
||||
if sqrtY == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Choose between `y` and it's negation
|
||||
is_largest := sqrtY.LexicographicallyLargest()
|
||||
if choose_largest == is_largest {
|
||||
return sqrtY
|
||||
} else {
|
||||
return sqrtY.Neg(sqrtY)
|
||||
}
|
||||
}
|
||||
|
||||
// PointExtendedFromProj converts a point in projective coordinates to extended coordinates.
|
||||
func PointExtendedFromProj(p *PointProj) PointExtended {
|
||||
var pzinv fp.Element
|
||||
pzinv.Inverse(&p.Z)
|
||||
var z fp.Element
|
||||
z.Mul(&p.X, &p.Y).Mul(&z, &pzinv)
|
||||
return PointExtended{
|
||||
X: p.X,
|
||||
Y: p.Y,
|
||||
Z: p.Z,
|
||||
T: z,
|
||||
}
|
||||
}
|
||||
|
||||
// PointExtendedNormalized is an extended point which is normalized.
|
||||
// i.e: Z=1. We store it this way to save 32 bytes per point in memory.
|
||||
type PointExtendedNormalized struct {
|
||||
X, Y, T gnarkfr.Element
|
||||
}
|
||||
|
||||
// Neg computes p = -p1
|
||||
func (p *PointExtendedNormalized) Neg(p1 *PointExtendedNormalized) *PointExtendedNormalized {
|
||||
p.X.Neg(&p1.X)
|
||||
p.Y = p1.Y
|
||||
p.T.Neg(&p1.T)
|
||||
return p
|
||||
}
|
||||
|
||||
// ExtendedAddNormalized computes p = p1 + p2.
|
||||
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-madd-2008-hwcd
|
||||
func ExtendedAddNormalized(p, p1 *PointExtended, p2 *PointExtendedNormalized) *gnarkbandersnatch.PointExtended {
|
||||
var A, B, C, D, E, F, G, H, tmp gnarkfr.Element
|
||||
A.Mul(&p1.X, &p2.X)
|
||||
B.Mul(&p1.Y, &p2.Y)
|
||||
C.Mul(&p1.T, &p2.T).Mul(&C, &CurveParams.D)
|
||||
D.Set(&p1.Z)
|
||||
tmp.Add(&p1.X, &p1.Y)
|
||||
E.Add(&p2.X, &p2.Y).
|
||||
Mul(&E, &tmp).
|
||||
Sub(&E, &A).
|
||||
Sub(&E, &B)
|
||||
F.Sub(&D, &C)
|
||||
G.Add(&D, &C)
|
||||
H.Set(&A)
|
||||
|
||||
// mulBy5(&H)
|
||||
H.Neg(&H)
|
||||
gnarkfr.MulBy5(&H)
|
||||
|
||||
H.Sub(&B, &H)
|
||||
|
||||
p.X.Mul(&E, &F)
|
||||
p.Y.Mul(&G, &H)
|
||||
p.T.Mul(&E, &H)
|
||||
p.Z.Mul(&F, &G)
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
package fp
|
||||
|
||||
import (
|
||||
basefield "github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
)
|
||||
|
||||
// Element is a field element representing the basefield of the curve.
|
||||
type Element = basefield.Element
|
||||
|
||||
// Limbs is the number of 64-bit words needed to represent the field element.
|
||||
const Limbs = basefield.Limbs
|
||||
|
||||
// One returns the field element 1.
|
||||
func One() Element {
|
||||
return basefield.One()
|
||||
}
|
||||
|
||||
// Zero returns the field element 0.
|
||||
func Zero() Element {
|
||||
return basefield.Element{}
|
||||
}
|
||||
|
||||
// MinusOne returns the field element -1.
|
||||
func MinusOne() Element {
|
||||
m_one := One()
|
||||
m_one.Neg(&m_one)
|
||||
return m_one
|
||||
}
|
||||
|
||||
// MulBy5 multiplies a field element by 5.
|
||||
func MulBy5(a *Element) {
|
||||
basefield.MulBy5(a)
|
||||
}
|
||||
|
||||
// BatchInvert computes the inverse of a slice of field elements.
|
||||
func BatchInvert(a []Element) []Element {
|
||||
return basefield.BatchInvert(a)
|
||||
}
|
||||
|
||||
// BytesLE returns the little-endian byte representation of a field element.
|
||||
func BytesLE(a Element) []byte {
|
||||
var result [basefield.Bytes]byte
|
||||
basefield.LittleEndian.PutElement(&result, a)
|
||||
return result[:]
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
package fp
|
||||
|
||||
import "math/big"
|
||||
|
||||
// The following code is _almost_ the original code from:
|
||||
// https://github.com/GottfriedHerold/Bandersnatch/blob/f665f90b64892b9c4c89cff3219e70456bb431e5/bandersnatch/fieldElements/field_element_square_root.go
|
||||
//
|
||||
// We had to do some changes to make it work with gnark:
|
||||
// - The type `feType_SquareRoot` was aliased to `Element` so everything looks the same. These types didn't have the exact
|
||||
// same underlying representation, so it leaded to some minor adjustements. (e.g: accessing the limbs)
|
||||
// - Original APIs regarding finite-field multiplications (e.g: MulEq) were adjusted to use gnark Mul APIs.
|
||||
// - The original code had to explicitly do `Normalize()` after field element operations, but this isn't needed in gnark.
|
||||
// - The primitive 2^32-root-of unity value (see init()) was pulled from gnark FFT domain code.
|
||||
// - The original code used anonymous functions to define global vars, but we changed to use a init() function.
|
||||
// This was required since we have other init() in the package that configure other globals (e.g: _modulus).
|
||||
// By the way init() functions execution order works, we'll have these configured before the sqrt init() is called,
|
||||
// compared with the original anonymous function global calls.
|
||||
|
||||
type feType_SquareRoot = Element
|
||||
|
||||
const (
|
||||
BaseField2Adicity = 32
|
||||
sqrtParam_TotalBits = BaseField2Adicity // (p-1) = n^Q. 2^S with Q odd, leads to S = 32.
|
||||
sqrtParam_BlockSize = 8 // 8 bit window per chunk
|
||||
sqrtParam_Blocks = sqrtParam_TotalBits / sqrtParam_BlockSize
|
||||
sqrtParam_FirstBlockUnusedBits = sqrtParam_Blocks*sqrtParam_BlockSize - sqrtParam_TotalBits // number of unused bits in the first reconstructed block.
|
||||
sqrtParam_BitMask = (1 << sqrtParam_BlockSize) - 1 // bitmask to pick up the last sqrtParam_BlockSize bits.
|
||||
)
|
||||
|
||||
// NOTE: These "variables" are actually pre-computed constants that must not change.
|
||||
var (
|
||||
// sqrtPrecomp_PrimitiveDyadicRoots[i] equals DyadicRootOfUnity^(2^i) for 0 <= i <= 32
|
||||
//
|
||||
// This means that it is a 32-i'th primitive root of unitity, obtained by repeatedly squaring a 2^32th primitive root of unity [DyadicRootOfUnity_fe].
|
||||
sqrtPrecomp_PrimitiveDyadicRoots [BaseField2Adicity + 1]feType_SquareRoot
|
||||
|
||||
// primitive root of unity of order 2^sqrtParam_BlockSize
|
||||
sqrtPrecomp_ReconstructionDyadicRoot feType_SquareRoot
|
||||
|
||||
// sqrtPrecomp_dlogLUT is a lookup table used to implement the map sqrtPrecompt_reconstructionDyadicRoot^a -> -a
|
||||
sqrtPrecomp_dlogLUT map[uint16]uint
|
||||
)
|
||||
|
||||
func init() {
|
||||
sqrtPrecomp_PrimitiveDyadicRoots = func() (ret [BaseField2Adicity + 1]feType_SquareRoot) {
|
||||
if _, err := ret[0].SetString("10238227357739495823651030575849232062558860180284477541189508159991286009131"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i := 1; i <= BaseField2Adicity; i++ { // Note <= here
|
||||
ret[i].Square(&ret[i-1])
|
||||
}
|
||||
// 31th one must be -1. We check that here.
|
||||
x := big.NewInt(0)
|
||||
ret[BaseField2Adicity-1].BigInt(x)
|
||||
if ret[BaseField2Adicity-1].String() != "-1" {
|
||||
panic("something is wrong with the dyadic roots of unity")
|
||||
}
|
||||
return
|
||||
}() // immediately invoked lambda
|
||||
sqrtPrecomp_ReconstructionDyadicRoot = sqrtPrecomp_PrimitiveDyadicRoots[BaseField2Adicity-sqrtParam_BlockSize]
|
||||
sqrtPrecomp_PrecomputedBlocks = func() (blocks [sqrtParam_Blocks][1 << sqrtParam_BlockSize]feType_SquareRoot) {
|
||||
for i := 0; i < sqrtParam_Blocks; i++ {
|
||||
blocks[i][0].SetOne()
|
||||
for j := 1; j < (1 << sqrtParam_BlockSize); j++ {
|
||||
blocks[i][j].Mul(&blocks[i][j-1], &sqrtPrecomp_PrimitiveDyadicRoots[i*sqrtParam_BlockSize])
|
||||
}
|
||||
}
|
||||
return
|
||||
}() // immediately invoked lambda
|
||||
|
||||
sqrtPrecomp_dlogLUT = func() (ret map[uint16]uint) {
|
||||
const LUTSize = 1 << sqrtParam_BlockSize // 256
|
||||
ret = make(map[uint16]uint, LUTSize)
|
||||
|
||||
var rootOfUnity feType_SquareRoot
|
||||
rootOfUnity.SetOne()
|
||||
for i := 0; i < LUTSize; i++ {
|
||||
const mask = LUTSize - 1
|
||||
// the LUTSize many roots of unity all (by chance) have distinct values for .words[0]&0xFFFF. Note that this uses the Montgomery representation.
|
||||
ret[uint16(rootOfUnity[0]&0xFFFF)] = uint((-i) & mask)
|
||||
rootOfUnity.Mul(&rootOfUnity, &sqrtPrecomp_ReconstructionDyadicRoot)
|
||||
}
|
||||
// This effectively checks the above claim (that .words[0]&0xFFFF is distinct).
|
||||
// Note that this might fail if we adjust the sqrtParam_BlockSize parameter and this check will alert us.
|
||||
if len(ret) != LUTSize {
|
||||
panic("failed to store all appropriate roots of unity in a map")
|
||||
}
|
||||
return
|
||||
}() // immediately invoked lambda
|
||||
}
|
||||
|
||||
// sqrtAlg_NegDlogInSmallDyadicSubgroup takes a (not necessarily primitive) root of unity x of order 2^sqrtParam_BlockSize.
|
||||
// x has the form sqrtPrecomp_ReconstructionDyadicRoot^a and returns its negative dlog -a.
|
||||
//
|
||||
// The returned value is only meaningful modulo 1<<sqrtParam_BlockSize and is fully reduced, i.e. in [0, 1<<sqrtParam_BlockSize )
|
||||
//
|
||||
// NOTE: If x is not a root of unity as asserted, the behaviour is undefined.
|
||||
func sqrtAlg_NegDlogInSmallDyadicSubgroup(x *feType_SquareRoot) uint {
|
||||
return sqrtPrecomp_dlogLUT[uint16(x[0]&0xFFFF)]
|
||||
}
|
||||
|
||||
// sqrtAlg_GetPrecomputedRootOfUnity sets target to g^(multiplier << (order * sqrtParam_BlockSize)), where g is the fixed primitive 2^32th root of unity.
|
||||
//
|
||||
// We assume that order 0 <= order*sqrtParam_BlockSize <= 32 and that multiplier is in [0, 1 <<sqrtParam_BlockSize)
|
||||
func sqrtAlg_GetPrecomputedRootOfUnity(target *feType_SquareRoot, multiplier int, order uint) {
|
||||
*target = sqrtPrecomp_PrecomputedBlocks[order][multiplier]
|
||||
}
|
||||
|
||||
// sqrtPrecomp_PrecomputedBlocks[i][j] == g^(j << (i* BlockSize)), where g is the fixed primitive 2^32th root of unity.
|
||||
// This means that the exponent is equal to 0x00000...0000jjjjjj0000....0000, where only the i'th least significant block of size BlockSize is set
|
||||
// and that value is j.
|
||||
//
|
||||
// Note: accessed through sqrtAlg_getPrecomputedRootOfUnity
|
||||
var sqrtPrecomp_PrecomputedBlocks [sqrtParam_Blocks][1 << sqrtParam_BlockSize]feType_SquareRoot
|
||||
|
||||
func SqrtPrecomp(x *Element) *Element {
|
||||
res := Zero()
|
||||
if x.IsZero() {
|
||||
return &res
|
||||
}
|
||||
var xCopy feType_SquareRoot = *x
|
||||
var candidate, rootOfUnity feType_SquareRoot
|
||||
sqrtAlg_ComputeRelevantPowers(&xCopy, &candidate, &rootOfUnity)
|
||||
if !invSqrtEqDyadic(&rootOfUnity) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return res.Mul(&candidate, &rootOfUnity)
|
||||
}
|
||||
|
||||
func invSqrtEqDyadic(z *Element) bool {
|
||||
// The algorithm works by essentially computing the dlog of z and then halving it.
|
||||
|
||||
// negExponent is intended to hold the negative of the dlog of z.
|
||||
// We determine this 32-bit value (usually) _sqrtBlockSize many bits at a time, starting with the least-significant bits.
|
||||
//
|
||||
// If _sqrtBlockSize does not divide 32, the *first* iteration will determine fewer bits.
|
||||
var negExponent uint
|
||||
|
||||
var temp, temp2 feType_SquareRoot
|
||||
|
||||
// set powers[i] to z^(1<< (i*blocksize))
|
||||
var powers [sqrtParam_Blocks]feType_SquareRoot
|
||||
powers[0] = *z
|
||||
for i := 1; i < sqrtParam_Blocks; i++ {
|
||||
powers[i] = powers[i-1]
|
||||
for j := 0; j < sqrtParam_BlockSize; j++ {
|
||||
powers[i].Square(&powers[i])
|
||||
}
|
||||
}
|
||||
|
||||
// looking at the dlogs, powers[i] is essentially the wanted exponent, left-shifted by i*_sqrtBlockSize and taken mod 1<<32
|
||||
// dlogHighDyadicRootNeg essentially (up to sign) reads off the _sqrtBlockSize many most significant bits. (returned as low-order bits)
|
||||
|
||||
// first iteration may be slightly special if BlockSize does not divide 32
|
||||
negExponent = sqrtAlg_NegDlogInSmallDyadicSubgroup(&powers[sqrtParam_Blocks-1])
|
||||
negExponent >>= sqrtParam_FirstBlockUnusedBits
|
||||
|
||||
// if the exponent we just got is odd, there is no square root, no point in determining the other bits.
|
||||
if negExponent&1 == 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get remaining bits
|
||||
for i := 1; i < sqrtParam_Blocks; i++ {
|
||||
temp2 = powers[sqrtParam_Blocks-1-i]
|
||||
// We essentially un-set the bits we already know from powers[_sqrtNumBlocks-1-i]
|
||||
for j := 0; j < i; j++ {
|
||||
sqrtAlg_GetPrecomputedRootOfUnity(&temp, int((negExponent>>(j*sqrtParam_BlockSize))&sqrtParam_BitMask), uint(j+sqrtParam_Blocks-1-i))
|
||||
temp2.Mul(&temp2, &temp)
|
||||
}
|
||||
newBits := sqrtAlg_NegDlogInSmallDyadicSubgroup(&temp2)
|
||||
negExponent |= newBits << (sqrtParam_BlockSize*i - sqrtParam_FirstBlockUnusedBits)
|
||||
}
|
||||
|
||||
// var tmp _FESquareRoot
|
||||
|
||||
// negExponent is now the negative dlog of z.
|
||||
|
||||
// Take the square root
|
||||
negExponent >>= 1
|
||||
// Write to z:
|
||||
z.SetOne()
|
||||
for i := 0; i < sqrtParam_Blocks; i++ {
|
||||
sqrtAlg_GetPrecomputedRootOfUnity(&temp, int((negExponent>>(i*sqrtParam_BlockSize))&sqrtParam_BitMask), uint(i))
|
||||
z.Mul(z, &temp)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func sqrtAlg_ComputeRelevantPowers(z *Element, squareRootCandidate *feType_SquareRoot, rootOfUnity *feType_SquareRoot) {
|
||||
SquareEqNTimes := func(z *feType_SquareRoot, n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
z.Square(z)
|
||||
}
|
||||
}
|
||||
|
||||
// hand-crafted sliding window-type algorithm with window-size 5
|
||||
// Note that we precompute and use z^255 multiple times (even though it's not size 5)
|
||||
// and some windows actually overlap(!)
|
||||
|
||||
var z2, z3, z7, z6, z9, z11, z13, z19, z21, z25, z27, z29, z31, z255 feType_SquareRoot
|
||||
var acc feType_SquareRoot
|
||||
z2.Square(z) // 0b10
|
||||
z3.Mul(z, &z2) // 0b11
|
||||
z6.Square(&z3) // 0b110
|
||||
z7.Mul(z, &z6) // 0b111
|
||||
z9.Mul(&z7, &z2) // 0b1001
|
||||
z11.Mul(&z9, &z2) // 0b1011
|
||||
z13.Mul(&z11, &z2) // 0b1101
|
||||
z19.Mul(&z13, &z6) // 0b10011
|
||||
z21.Mul(&z2, &z19) // 0b10101
|
||||
z25.Mul(&z19, &z6) // 0b11001
|
||||
z27.Mul(&z25, &z2) // 0b11011
|
||||
z29.Mul(&z27, &z2) // 0b11101
|
||||
z31.Mul(&z29, &z2) // 0b11111
|
||||
acc.Mul(&z27, &z29) // 56
|
||||
acc.Square(&acc) // 112
|
||||
acc.Square(&acc) // 224
|
||||
z255.Mul(&acc, &z31) // 0b11111111 = 255
|
||||
acc.Square(&acc) // 448
|
||||
acc.Square(&acc) // 896
|
||||
acc.Mul(&acc, &z31) // 0b1110011111 = 927
|
||||
SquareEqNTimes(&acc, 6) // 0b1110011111000000
|
||||
acc.Mul(&acc, &z27) // 0b1110011111011011
|
||||
SquareEqNTimes(&acc, 6) // 0b1110011111011011000000
|
||||
acc.Mul(&acc, &z19) // 0b1110011111011011010011
|
||||
SquareEqNTimes(&acc, 5) // 0b111001111101101101001100000
|
||||
acc.Mul(&acc, &z21) // 0b111001111101101101001110101
|
||||
SquareEqNTimes(&acc, 7) // 0b1110011111011011010011101010000000
|
||||
acc.Mul(&acc, &z25) // 0b1110011111011011010011101010011001
|
||||
SquareEqNTimes(&acc, 6) // 0b1110011111011011010011101010011001000000
|
||||
acc.Mul(&acc, &z19) // 0b1110011111011011010011101010011001010011
|
||||
SquareEqNTimes(&acc, 5) // 0b111001111101101101001110101001100101001100000
|
||||
acc.Mul(&acc, &z7) // 0b111001111101101101001110101001100101001100111
|
||||
SquareEqNTimes(&acc, 5) // 0b11100111110110110100111010100110010100110011100000
|
||||
acc.Mul(&acc, &z11) // 0b11100111110110110100111010100110010100110011101011
|
||||
SquareEqNTimes(&acc, 5) // 0b1110011111011011010011101010011001010011001110101100000
|
||||
acc.Mul(&acc, &z29) // 0b1110011111011011010011101010011001010011001110101111101
|
||||
SquareEqNTimes(&acc, 5) // 0b111001111101101101001110101001100101001100111010111110100000
|
||||
acc.Mul(&acc, &z9) // 0b111001111101101101001110101001100101001100111010111110101001
|
||||
SquareEqNTimes(&acc, 7) // 0b1110011111011011010011101010011001010011001110101111101010010000000
|
||||
acc.Mul(&acc, &z3) // 0b1110011111011011010011101010011001010011001110101111101010010000011
|
||||
SquareEqNTimes(&acc, 7) // 0b11100111110110110100111010100110010100110011101011111010100100000110000000
|
||||
acc.Mul(&acc, &z25) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001
|
||||
SquareEqNTimes(&acc, 5) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100100000
|
||||
acc.Mul(&acc, &z25) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001
|
||||
SquareEqNTimes(&acc, 5) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100100000
|
||||
acc.Mul(&acc, &z27) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011
|
||||
SquareEqNTimes(&acc, 8) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000000
|
||||
acc.Mul(&acc, z) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001
|
||||
SquareEqNTimes(&acc, 8) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000000
|
||||
acc.Mul(&acc, z) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001
|
||||
SquareEqNTimes(&acc, 6) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001000000
|
||||
acc.Mul(&acc, &z13) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101
|
||||
SquareEqNTimes(&acc, 7) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000000
|
||||
acc.Mul(&acc, &z7) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111
|
||||
SquareEqNTimes(&acc, 3) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111000
|
||||
acc.Mul(&acc, &z3) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011
|
||||
SquareEqNTimes(&acc, 13) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000000000
|
||||
acc.Mul(&acc, &z21) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101
|
||||
SquareEqNTimes(&acc, 5) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010100000
|
||||
acc.Mul(&acc, &z9) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001
|
||||
SquareEqNTimes(&acc, 5) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100100000
|
||||
acc.Mul(&acc, &z27) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100111011
|
||||
SquareEqNTimes(&acc, 5) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101100000
|
||||
acc.Mul(&acc, &z27) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011
|
||||
SquareEqNTimes(&acc, 5) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101100000
|
||||
acc.Mul(&acc, &z9) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101101001
|
||||
SquareEqNTimes(&acc, 10) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000000
|
||||
acc.Mul(&acc, z) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000001
|
||||
SquareEqNTimes(&acc, 7) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100111011110110100100000000010000000
|
||||
acc.Mul(&acc, &z255) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100111011110110100100000000101111111
|
||||
SquareEqNTimes(&acc, 8) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111100000000
|
||||
acc.Mul(&acc, &z255) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111
|
||||
SquareEqNTimes(&acc, 6) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111000000
|
||||
acc.Mul(&acc, &z11) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011
|
||||
SquareEqNTimes(&acc, 9) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011000000000
|
||||
acc.Mul(&acc, &z255) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011011111111
|
||||
SquareEqNTimes(&acc, 2) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101101001000000001011111111111111100101101111111100
|
||||
acc.Mul(&acc, z) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101101001000000001011111111111111100101101111111101
|
||||
SquareEqNTimes(&acc, 7) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011011111111010000000
|
||||
acc.Mul(&acc, &z255) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011011111111101111111
|
||||
SquareEqNTimes(&acc, 8) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101101001000000001011111111111111100101101111111110111111100000000
|
||||
acc.Mul(&acc, &z255) // 0b11100111110110110100111010100110010100110011101011111010100100000110011001110011101100000001000000010011010000111011000000001010101001110111101101001000000001011111111111111100101101111111110111111111111111
|
||||
SquareEqNTimes(&acc, 8) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100111011110110100100000000101111111111111110010110111111111011111111111111100000000
|
||||
acc.Mul(&acc, &z255) // 0b1110011111011011010011101010011001010011001110101111101010010000011001100111001110110000000100000001001101000011101100000000101010100111011110110100100000000101111111111111110010110111111111011111111111111111111111
|
||||
SquareEqNTimes(&acc, 8) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011011111111101111111111111111111111100000000
|
||||
acc.Mul(&acc, &z255) // 0b111001111101101101001110101001100101001100111010111110101001000001100110011100111011000000010000000100110100001110110000000010101010011101111011010010000000010111111111111111001011011111111101111111111111111111111111111111
|
||||
// acc is now z^((BaseFieldMultiplicativeOddOrder - 1)/2)
|
||||
rootOfUnity.Square(&acc) // BaseFieldMultiplicativeOddOrder - 1
|
||||
rootOfUnity.Mul(rootOfUnity, z) // BaseFieldMultiplicativeOddOrder
|
||||
squareRootCandidate.Mul(&acc, z) // (BaseFieldMultiplicativeOddOrder + 1)/2
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
package fp
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCustomSqrt(t *testing.T) {
|
||||
for i := 0; i < 10_000; i++ {
|
||||
// Take a random fp.
|
||||
var a Element
|
||||
a.SetUint64(uint64(i))
|
||||
|
||||
// Calculate the square root with thew optimized algorithm.
|
||||
sqrtNew := SqrtPrecomp(&a)
|
||||
if sqrtNew == nil {
|
||||
continue // Doesn't exist? Skip.
|
||||
}
|
||||
|
||||
// Recalculate the original element using the calculated sqrt.
|
||||
var regenNew Element
|
||||
regenNew.Mul(sqrtNew, sqrtNew)
|
||||
|
||||
// Check the obvious: regenNew should be equal to the original element.
|
||||
if !regenNew.Equal(&a) {
|
||||
t.Fatalf("regenNew != a for %d", i)
|
||||
}
|
||||
|
||||
// Calculate the sqrt with the original gnark code.
|
||||
var sqrtOld Element
|
||||
sqrtOld.Sqrt(&a)
|
||||
var regenOld Element
|
||||
regenOld.Mul(&sqrtOld, &sqrtOld)
|
||||
if !regenOld.Equal(&a) { // Somewhat obvious, but still.
|
||||
t.Fatalf("regenOld != a for %d", i)
|
||||
}
|
||||
|
||||
// Check that both sqrt's are equal, *considering* the case that they have opposite signs.
|
||||
// We need to do that since both algorithm can return either the positive or negative sqrt,
|
||||
// which is fine.
|
||||
if !sqrtNew.Equal(&sqrtOld) && !sqrtNew.Neg(sqrtNew).Equal(&sqrtOld) {
|
||||
t.Fatalf("sqrtNew != sqrtOld for %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
// 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 (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// madd0 hi = a*b + c (discards lo bits)
|
||||
func madd0(a, b, c uint64) (hi uint64) {
|
||||
var carry, lo uint64
|
||||
hi, lo = bits.Mul64(a, b)
|
||||
_, carry = bits.Add64(lo, c, 0)
|
||||
hi, _ = bits.Add64(hi, 0, carry)
|
||||
return
|
||||
}
|
||||
|
||||
// madd1 hi, lo = a*b + c
|
||||
func madd1(a, b, c uint64) (hi uint64, lo uint64) {
|
||||
var carry uint64
|
||||
hi, lo = bits.Mul64(a, b)
|
||||
lo, carry = bits.Add64(lo, c, 0)
|
||||
hi, _ = bits.Add64(hi, 0, carry)
|
||||
return
|
||||
}
|
||||
|
||||
// madd2 hi, lo = a*b + c + d
|
||||
func madd2(a, b, c, d uint64) (hi uint64, lo uint64) {
|
||||
var carry uint64
|
||||
hi, lo = bits.Mul64(a, b)
|
||||
c, carry = bits.Add64(c, d, 0)
|
||||
hi, _ = bits.Add64(hi, 0, carry)
|
||||
lo, carry = bits.Add64(lo, c, 0)
|
||||
hi, _ = bits.Add64(hi, 0, carry)
|
||||
return
|
||||
}
|
||||
|
||||
func madd3(a, b, c, d, e uint64) (hi uint64, lo uint64) {
|
||||
var carry uint64
|
||||
hi, lo = bits.Mul64(a, b)
|
||||
c, carry = bits.Add64(c, d, 0)
|
||||
hi, _ = bits.Add64(hi, 0, carry)
|
||||
lo, carry = bits.Add64(lo, c, 0)
|
||||
hi, _ = bits.Add64(hi, e, carry)
|
||||
return
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
//go:build !noadx
|
||||
// +build !noadx
|
||||
|
||||
// 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 "golang.org/x/sys/cpu"
|
||||
|
||||
var supportAdx = cpu.X86.HasADX && cpu.X86.HasBMI2
|
||||
@@ -1,25 +0,0 @@
|
||||
//go:build noadx
|
||||
// +build noadx
|
||||
|
||||
// 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
|
||||
|
||||
// note: this is needed for test purposes, as dynamically changing supportAdx doesn't flag
|
||||
// certain errors (like fatal error: missing stackmap)
|
||||
// this ensures we test all asm path.
|
||||
var supportAdx = false
|
||||
@@ -1,47 +0,0 @@
|
||||
// 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 = 0x1cfb69...76e7e1.
|
||||
//
|
||||
// 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/@zkteam/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
|
||||
//
|
||||
// 0x1cfb69d4ca675f520cce760202687600ff8f87007419047174fd06b52876e7e1 // base 16
|
||||
// 13108968793781547619861935127046491459309155893440570251786403306729687672801 // base 10
|
||||
package fr
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,136 +0,0 @@
|
||||
//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]
|
||||
}
|
||||
@@ -1,466 +0,0 @@
|
||||
// +build amd64_adx
|
||||
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// modulus q
|
||||
DATA q<>+0(SB)/8, $0x74fd06b52876e7e1
|
||||
DATA q<>+8(SB)/8, $0xff8f870074190471
|
||||
DATA q<>+16(SB)/8, $0x0cce760202687600
|
||||
DATA q<>+24(SB)/8, $0x1cfb69d4ca675f52
|
||||
GLOBL q<>(SB), (RODATA+NOPTR), $32
|
||||
|
||||
// qInv0 q'[0]
|
||||
DATA qInv0<>(SB)/8, $0xf19f22295cc063df
|
||||
GLOBL qInv0<>(SB), (RODATA+NOPTR), $8
|
||||
|
||||
#define REDUCE(ra0, ra1, ra2, ra3, rb0, rb1, rb2, rb3) \
|
||||
MOVQ ra0, rb0; \
|
||||
SUBQ q<>(SB), ra0; \
|
||||
MOVQ ra1, rb1; \
|
||||
SBBQ q<>+8(SB), ra1; \
|
||||
MOVQ ra2, rb2; \
|
||||
SBBQ q<>+16(SB), ra2; \
|
||||
MOVQ ra3, rb3; \
|
||||
SBBQ q<>+24(SB), ra3; \
|
||||
CMOVQCS rb0, ra0; \
|
||||
CMOVQCS rb1, ra1; \
|
||||
CMOVQCS rb2, ra2; \
|
||||
CMOVQCS rb3, ra3; \
|
||||
|
||||
// mul(res, x, y *Element)
|
||||
TEXT ·mul(SB), NOSPLIT, $0-24
|
||||
|
||||
// the algorithm is described here
|
||||
// https://hackmd.io/@zkteam/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
|
||||
// for j=0 to N-1
|
||||
// (A,t[j]) := t[j] + x[j]*y[i] + A
|
||||
// m := t[0]*q'[0] mod W
|
||||
// C,_ := t[0] + m*q[0]
|
||||
// for j=1 to N-1
|
||||
// (C,t[j-1]) := t[j] + m*q[j] + C
|
||||
// t[N-1] = C + A
|
||||
|
||||
MOVQ x+8(FP), SI
|
||||
|
||||
// x[0] -> DI
|
||||
// x[1] -> R8
|
||||
// x[2] -> R9
|
||||
// x[3] -> R10
|
||||
MOVQ 0(SI), DI
|
||||
MOVQ 8(SI), R8
|
||||
MOVQ 16(SI), R9
|
||||
MOVQ 24(SI), R10
|
||||
MOVQ y+16(FP), R11
|
||||
|
||||
// A -> BP
|
||||
// t[0] -> R14
|
||||
// t[1] -> R15
|
||||
// t[2] -> CX
|
||||
// t[3] -> BX
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 0(R11), DX
|
||||
|
||||
// (A,t[0]) := x[0]*y[0] + A
|
||||
MULXQ DI, R14, R15
|
||||
|
||||
// (A,t[1]) := x[1]*y[0] + A
|
||||
MULXQ R8, AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := x[2]*y[0] + A
|
||||
MULXQ R9, AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := x[3]*y[0] + A
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 8(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[1] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[1] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[1] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[1] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 16(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[2] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[2] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[2] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[2] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 24(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[3] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[3] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[3] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[3] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
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)
|
||||
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ R14, 0(AX)
|
||||
MOVQ R15, 8(AX)
|
||||
MOVQ CX, 16(AX)
|
||||
MOVQ BX, 24(AX)
|
||||
RET
|
||||
|
||||
TEXT ·fromMont(SB), NOSPLIT, $0-8
|
||||
|
||||
// the algorithm is described here
|
||||
// https://hackmd.io/@zkteam/modular_multiplication
|
||||
// when y = 1 we have:
|
||||
// for i=0 to N-1
|
||||
// t[i] = x[i]
|
||||
// for i=0 to N-1
|
||||
// m := t[0]*q'[0] mod W
|
||||
// C,_ := t[0] + m*q[0]
|
||||
// for j=1 to N-1
|
||||
// (C,t[j-1]) := t[j] + m*q[j] + C
|
||||
// t[N-1] = C
|
||||
MOVQ res+0(FP), DX
|
||||
MOVQ 0(DX), R14
|
||||
MOVQ 8(DX), R15
|
||||
MOVQ 16(DX), CX
|
||||
MOVQ 24(DX), BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
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)
|
||||
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ R14, 0(AX)
|
||||
MOVQ R15, 8(AX)
|
||||
MOVQ CX, 16(AX)
|
||||
MOVQ BX, 24(AX)
|
||||
RET
|
||||
@@ -1,488 +0,0 @@
|
||||
// +build !amd64_adx
|
||||
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// modulus q
|
||||
DATA q<>+0(SB)/8, $0x74fd06b52876e7e1
|
||||
DATA q<>+8(SB)/8, $0xff8f870074190471
|
||||
DATA q<>+16(SB)/8, $0x0cce760202687600
|
||||
DATA q<>+24(SB)/8, $0x1cfb69d4ca675f52
|
||||
GLOBL q<>(SB), (RODATA+NOPTR), $32
|
||||
|
||||
// qInv0 q'[0]
|
||||
DATA qInv0<>(SB)/8, $0xf19f22295cc063df
|
||||
GLOBL qInv0<>(SB), (RODATA+NOPTR), $8
|
||||
|
||||
#define REDUCE(ra0, ra1, ra2, ra3, rb0, rb1, rb2, rb3) \
|
||||
MOVQ ra0, rb0; \
|
||||
SUBQ q<>(SB), ra0; \
|
||||
MOVQ ra1, rb1; \
|
||||
SBBQ q<>+8(SB), ra1; \
|
||||
MOVQ ra2, rb2; \
|
||||
SBBQ q<>+16(SB), ra2; \
|
||||
MOVQ ra3, rb3; \
|
||||
SBBQ q<>+24(SB), ra3; \
|
||||
CMOVQCS rb0, ra0; \
|
||||
CMOVQCS rb1, ra1; \
|
||||
CMOVQCS rb2, ra2; \
|
||||
CMOVQCS rb3, ra3; \
|
||||
|
||||
// mul(res, x, y *Element)
|
||||
TEXT ·mul(SB), $24-24
|
||||
|
||||
// the algorithm is described here
|
||||
// https://hackmd.io/@zkteam/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
|
||||
// for j=0 to N-1
|
||||
// (A,t[j]) := t[j] + x[j]*y[i] + A
|
||||
// m := t[0]*q'[0] mod W
|
||||
// C,_ := t[0] + m*q[0]
|
||||
// for j=1 to N-1
|
||||
// (C,t[j-1]) := t[j] + m*q[j] + C
|
||||
// t[N-1] = C + A
|
||||
|
||||
NO_LOCAL_POINTERS
|
||||
CMPB ·supportAdx(SB), $1
|
||||
JNE l1
|
||||
MOVQ x+8(FP), SI
|
||||
|
||||
// x[0] -> DI
|
||||
// x[1] -> R8
|
||||
// x[2] -> R9
|
||||
// x[3] -> R10
|
||||
MOVQ 0(SI), DI
|
||||
MOVQ 8(SI), R8
|
||||
MOVQ 16(SI), R9
|
||||
MOVQ 24(SI), R10
|
||||
MOVQ y+16(FP), R11
|
||||
|
||||
// A -> BP
|
||||
// t[0] -> R14
|
||||
// t[1] -> R15
|
||||
// t[2] -> CX
|
||||
// t[3] -> BX
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 0(R11), DX
|
||||
|
||||
// (A,t[0]) := x[0]*y[0] + A
|
||||
MULXQ DI, R14, R15
|
||||
|
||||
// (A,t[1]) := x[1]*y[0] + A
|
||||
MULXQ R8, AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := x[2]*y[0] + A
|
||||
MULXQ R9, AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := x[3]*y[0] + A
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 8(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[1] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[1] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[1] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[1] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 16(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[2] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[2] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[2] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[2] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ BP, BX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
MOVQ 24(R11), DX
|
||||
|
||||
// (A,t[0]) := t[0] + x[0]*y[3] + A
|
||||
MULXQ DI, AX, BP
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (A,t[1]) := t[1] + x[1]*y[3] + A
|
||||
ADCXQ BP, R15
|
||||
MULXQ R8, AX, BP
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (A,t[2]) := t[2] + x[2]*y[3] + A
|
||||
ADCXQ BP, CX
|
||||
MULXQ R9, AX, BP
|
||||
ADOXQ AX, CX
|
||||
|
||||
// (A,t[3]) := t[3] + x[3]*y[3] + A
|
||||
ADCXQ BP, BX
|
||||
MULXQ R10, AX, BP
|
||||
ADOXQ AX, BX
|
||||
|
||||
// A += carries from ADCXQ and ADOXQ
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BP
|
||||
ADOXQ AX, BP
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
|
||||
// clear the flags
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, R12
|
||||
ADCXQ R14, AX
|
||||
MOVQ R12, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
|
||||
// t[3] = C + A
|
||||
MOVQ $0, AX
|
||||
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)
|
||||
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ R14, 0(AX)
|
||||
MOVQ R15, 8(AX)
|
||||
MOVQ CX, 16(AX)
|
||||
MOVQ BX, 24(AX)
|
||||
RET
|
||||
|
||||
l1:
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ AX, (SP)
|
||||
MOVQ x+8(FP), AX
|
||||
MOVQ AX, 8(SP)
|
||||
MOVQ y+16(FP), AX
|
||||
MOVQ AX, 16(SP)
|
||||
CALL ·_mulGeneric(SB)
|
||||
RET
|
||||
|
||||
TEXT ·fromMont(SB), $8-8
|
||||
NO_LOCAL_POINTERS
|
||||
|
||||
// the algorithm is described here
|
||||
// https://hackmd.io/@zkteam/modular_multiplication
|
||||
// when y = 1 we have:
|
||||
// for i=0 to N-1
|
||||
// t[i] = x[i]
|
||||
// for i=0 to N-1
|
||||
// m := t[0]*q'[0] mod W
|
||||
// C,_ := t[0] + m*q[0]
|
||||
// for j=1 to N-1
|
||||
// (C,t[j-1]) := t[j] + m*q[j] + C
|
||||
// t[N-1] = C
|
||||
CMPB ·supportAdx(SB), $1
|
||||
JNE l2
|
||||
MOVQ res+0(FP), DX
|
||||
MOVQ 0(DX), R14
|
||||
MOVQ 8(DX), R15
|
||||
MOVQ 16(DX), CX
|
||||
MOVQ 24(DX), BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
ADCXQ AX, BX
|
||||
ADOXQ AX, BX
|
||||
XORQ DX, DX
|
||||
|
||||
// m := t[0]*q'[0] mod W
|
||||
MOVQ qInv0<>(SB), DX
|
||||
IMULQ R14, DX
|
||||
XORQ AX, AX
|
||||
|
||||
// C,_ := t[0] + m*q[0]
|
||||
MULXQ q<>+0(SB), AX, BP
|
||||
ADCXQ R14, AX
|
||||
MOVQ BP, R14
|
||||
|
||||
// (C,t[0]) := t[1] + m*q[1] + C
|
||||
ADCXQ R15, R14
|
||||
MULXQ q<>+8(SB), AX, R15
|
||||
ADOXQ AX, R14
|
||||
|
||||
// (C,t[1]) := t[2] + m*q[2] + C
|
||||
ADCXQ CX, R15
|
||||
MULXQ q<>+16(SB), AX, CX
|
||||
ADOXQ AX, R15
|
||||
|
||||
// (C,t[2]) := t[3] + m*q[3] + C
|
||||
ADCXQ BX, CX
|
||||
MULXQ q<>+24(SB), AX, BX
|
||||
ADOXQ AX, CX
|
||||
MOVQ $0, AX
|
||||
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)
|
||||
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ R14, 0(AX)
|
||||
MOVQ R15, 8(AX)
|
||||
MOVQ CX, 16(AX)
|
||||
MOVQ BX, 24(AX)
|
||||
RET
|
||||
|
||||
l2:
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ AX, (SP)
|
||||
CALL ·_fromMontGeneric(SB)
|
||||
RET
|
||||
@@ -1,50 +0,0 @@
|
||||
// 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
|
||||
|
||||
//go:noescape
|
||||
func MulBy3(x *Element)
|
||||
|
||||
//go:noescape
|
||||
func MulBy5(x *Element)
|
||||
|
||||
//go:noescape
|
||||
func MulBy13(x *Element)
|
||||
|
||||
//go:noescape
|
||||
func add(res, x, y *Element)
|
||||
|
||||
//go:noescape
|
||||
func sub(res, x, y *Element)
|
||||
|
||||
//go:noescape
|
||||
func neg(res, x *Element)
|
||||
|
||||
//go:noescape
|
||||
func double(res, x *Element)
|
||||
|
||||
//go:noescape
|
||||
func mul(res, x, y *Element)
|
||||
|
||||
//go:noescape
|
||||
func fromMont(res *Element)
|
||||
|
||||
//go:noescape
|
||||
func reduce(res *Element)
|
||||
|
||||
//go:noescape
|
||||
func Butterfly(a, b *Element)
|
||||
@@ -1,340 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
// modulus q
|
||||
DATA q<>+0(SB)/8, $0x74fd06b52876e7e1
|
||||
DATA q<>+8(SB)/8, $0xff8f870074190471
|
||||
DATA q<>+16(SB)/8, $0x0cce760202687600
|
||||
DATA q<>+24(SB)/8, $0x1cfb69d4ca675f52
|
||||
GLOBL q<>(SB), (RODATA+NOPTR), $32
|
||||
|
||||
// qInv0 q'[0]
|
||||
DATA qInv0<>(SB)/8, $0xf19f22295cc063df
|
||||
GLOBL qInv0<>(SB), (RODATA+NOPTR), $8
|
||||
|
||||
#define REDUCE(ra0, ra1, ra2, ra3, rb0, rb1, rb2, rb3) \
|
||||
MOVQ ra0, rb0; \
|
||||
SUBQ q<>(SB), ra0; \
|
||||
MOVQ ra1, rb1; \
|
||||
SBBQ q<>+8(SB), ra1; \
|
||||
MOVQ ra2, rb2; \
|
||||
SBBQ q<>+16(SB), ra2; \
|
||||
MOVQ ra3, rb3; \
|
||||
SBBQ q<>+24(SB), ra3; \
|
||||
CMOVQCS rb0, ra0; \
|
||||
CMOVQCS rb1, ra1; \
|
||||
CMOVQCS rb2, ra2; \
|
||||
CMOVQCS rb3, ra3; \
|
||||
|
||||
// add(res, x, y *Element)
|
||||
TEXT ·add(SB), NOSPLIT, $0-24
|
||||
MOVQ x+8(FP), AX
|
||||
MOVQ 0(AX), CX
|
||||
MOVQ 8(AX), BX
|
||||
MOVQ 16(AX), SI
|
||||
MOVQ 24(AX), DI
|
||||
MOVQ y+16(FP), DX
|
||||
ADDQ 0(DX), CX
|
||||
ADCQ 8(DX), BX
|
||||
ADCQ 16(DX), SI
|
||||
ADCQ 24(DX), DI
|
||||
|
||||
// reduce element(CX,BX,SI,DI) using temp registers (R8,R9,R10,R11)
|
||||
REDUCE(CX,BX,SI,DI,R8,R9,R10,R11)
|
||||
|
||||
MOVQ res+0(FP), R12
|
||||
MOVQ CX, 0(R12)
|
||||
MOVQ BX, 8(R12)
|
||||
MOVQ SI, 16(R12)
|
||||
MOVQ DI, 24(R12)
|
||||
RET
|
||||
|
||||
// sub(res, x, y *Element)
|
||||
TEXT ·sub(SB), NOSPLIT, $0-24
|
||||
XORQ DI, DI
|
||||
MOVQ x+8(FP), SI
|
||||
MOVQ 0(SI), AX
|
||||
MOVQ 8(SI), DX
|
||||
MOVQ 16(SI), CX
|
||||
MOVQ 24(SI), BX
|
||||
MOVQ y+16(FP), SI
|
||||
SUBQ 0(SI), AX
|
||||
SBBQ 8(SI), DX
|
||||
SBBQ 16(SI), CX
|
||||
SBBQ 24(SI), BX
|
||||
MOVQ $0x74fd06b52876e7e1, R8
|
||||
MOVQ $0xff8f870074190471, R9
|
||||
MOVQ $0x0cce760202687600, R10
|
||||
MOVQ $0x1cfb69d4ca675f52, R11
|
||||
CMOVQCC DI, R8
|
||||
CMOVQCC DI, R9
|
||||
CMOVQCC DI, R10
|
||||
CMOVQCC DI, R11
|
||||
ADDQ R8, AX
|
||||
ADCQ R9, DX
|
||||
ADCQ R10, CX
|
||||
ADCQ R11, BX
|
||||
MOVQ res+0(FP), R12
|
||||
MOVQ AX, 0(R12)
|
||||
MOVQ DX, 8(R12)
|
||||
MOVQ CX, 16(R12)
|
||||
MOVQ BX, 24(R12)
|
||||
RET
|
||||
|
||||
// double(res, x *Element)
|
||||
TEXT ·double(SB), NOSPLIT, $0-16
|
||||
MOVQ x+8(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)
|
||||
|
||||
MOVQ res+0(FP), R11
|
||||
MOVQ DX, 0(R11)
|
||||
MOVQ CX, 8(R11)
|
||||
MOVQ BX, 16(R11)
|
||||
MOVQ SI, 24(R11)
|
||||
RET
|
||||
|
||||
// neg(res, x *Element)
|
||||
TEXT ·neg(SB), NOSPLIT, $0-16
|
||||
MOVQ res+0(FP), DI
|
||||
MOVQ x+8(FP), AX
|
||||
MOVQ 0(AX), DX
|
||||
MOVQ 8(AX), CX
|
||||
MOVQ 16(AX), BX
|
||||
MOVQ 24(AX), SI
|
||||
MOVQ DX, AX
|
||||
ORQ CX, AX
|
||||
ORQ BX, AX
|
||||
ORQ SI, AX
|
||||
TESTQ AX, AX
|
||||
JEQ l1
|
||||
MOVQ $0x74fd06b52876e7e1, R8
|
||||
SUBQ DX, R8
|
||||
MOVQ R8, 0(DI)
|
||||
MOVQ $0xff8f870074190471, R8
|
||||
SBBQ CX, R8
|
||||
MOVQ R8, 8(DI)
|
||||
MOVQ $0x0cce760202687600, R8
|
||||
SBBQ BX, R8
|
||||
MOVQ R8, 16(DI)
|
||||
MOVQ $0x1cfb69d4ca675f52, R8
|
||||
SBBQ SI, R8
|
||||
MOVQ R8, 24(DI)
|
||||
RET
|
||||
|
||||
l1:
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ AX, 8(DI)
|
||||
MOVQ AX, 16(DI)
|
||||
MOVQ AX, 24(DI)
|
||||
RET
|
||||
|
||||
TEXT ·reduce(SB), NOSPLIT, $0-8
|
||||
MOVQ res+0(FP), AX
|
||||
MOVQ 0(AX), DX
|
||||
MOVQ 8(AX), CX
|
||||
MOVQ 16(AX), BX
|
||||
MOVQ 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
|
||||
|
||||
// MulBy3(x *Element)
|
||||
TEXT ·MulBy3(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 0(AX), DX
|
||||
ADCQ 8(AX), CX
|
||||
ADCQ 16(AX), BX
|
||||
ADCQ 24(AX), 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, 0(AX)
|
||||
MOVQ CX, 8(AX)
|
||||
MOVQ BX, 16(AX)
|
||||
MOVQ SI, 24(AX)
|
||||
RET
|
||||
|
||||
// MulBy5(x *Element)
|
||||
TEXT ·MulBy5(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)
|
||||
|
||||
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 (R15,DI,R8,R9)
|
||||
REDUCE(DX,CX,BX,SI,R15,DI,R8,R9)
|
||||
|
||||
MOVQ DX, 0(AX)
|
||||
MOVQ CX, 8(AX)
|
||||
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 $0x74fd06b52876e7e1, R12
|
||||
MOVQ $0xff8f870074190471, R13
|
||||
MOVQ $0x0cce760202687600, R14
|
||||
MOVQ $0x1cfb69d4ca675f52, 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
|
||||
@@ -1,75 +0,0 @@
|
||||
//go:build !amd64
|
||||
// +build !amd64
|
||||
|
||||
// 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
|
||||
|
||||
// Security Note: Field arithmetic uses constant-time conditional operations
|
||||
// to prevent timing side-channel attacks. See element.go for details.
|
||||
|
||||
// MulBy3 x *= 3
|
||||
func MulBy3(x *Element) {
|
||||
mulByConstant(x, 3)
|
||||
}
|
||||
|
||||
// MulBy5 x *= 5
|
||||
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)
|
||||
}
|
||||
|
||||
// FromMont converts z in place (i.e. mutates) from Montgomery to regular representation
|
||||
// sets and returns z = z * 1
|
||||
func fromMont(z *Element) {
|
||||
_fromMontGeneric(z)
|
||||
}
|
||||
|
||||
func add(z, x, y *Element) {
|
||||
_addGeneric(z, x, y)
|
||||
}
|
||||
|
||||
func double(z, x *Element) {
|
||||
_doubleGeneric(z, x)
|
||||
}
|
||||
|
||||
func sub(z, x, y *Element) {
|
||||
_subGeneric(z, x, y)
|
||||
}
|
||||
|
||||
func neg(z, x *Element) {
|
||||
_negGeneric(z, x)
|
||||
}
|
||||
|
||||
func reduce(z *Element) {
|
||||
_reduceGeneric(z)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,717 +0,0 @@
|
||||
package bandersnatch
|
||||
|
||||
// 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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/bits"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
gnarkbandersnatch "github.com/consensys/gnark-crypto/ecc/bls12-381/bandersnatch"
|
||||
"github.com/leanovate/gopter"
|
||||
"github.com/leanovate/gopter/prop"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
)
|
||||
|
||||
// GenFr generates an Fr element
|
||||
func GenFr() gopter.Gen {
|
||||
return func(genParams *gopter.GenParameters) *gopter.GenResult {
|
||||
var elmt fr.Element
|
||||
var b [fr.Bytes]byte
|
||||
_, err := rand.Read(b[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
elmt.SetBytes(b[:])
|
||||
genResult := gopter.NewGenResult(elmt, gopter.NoShrinker)
|
||||
return genResult
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiExpPointAffine(t *testing.T) {
|
||||
|
||||
parameters := gopter.DefaultTestParameters()
|
||||
parameters.MinSuccessfulTests = 2
|
||||
|
||||
properties := gopter.NewProperties(parameters)
|
||||
|
||||
genScalar := GenFr()
|
||||
|
||||
var genAff = GetEdwardsCurve().Base
|
||||
var Generator PointProj
|
||||
Generator.FromAffine(&genAff)
|
||||
|
||||
// size of the multiExps
|
||||
const nbSamples = 143
|
||||
|
||||
// multi exp points
|
||||
var samplePoints [nbSamples]PointAffine
|
||||
var g PointProj
|
||||
g.Set(&Generator)
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
samplePoints[i-1].FromProj(&g)
|
||||
g.Add(&g, &Generator)
|
||||
}
|
||||
|
||||
// final scalar to use in double and add method (without mixer factor)
|
||||
// n(n+1)(2n+1)/6 (sum of the squares from 1 to n)
|
||||
var scalar big.Int
|
||||
scalar.SetInt64(nbSamples)
|
||||
scalar.Mul(&scalar, new(big.Int).SetInt64(nbSamples+1))
|
||||
scalar.Mul(&scalar, new(big.Int).SetInt64(2*nbSamples+1))
|
||||
scalar.Div(&scalar, new(big.Int).SetInt64(6))
|
||||
|
||||
// ensure a multiexp that's splitted has the same result as a non-splitted one..
|
||||
properties.Property("[G1] Multi exponentation (c=16) should be consistant with splitted multiexp", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
var samplePointsLarge [nbSamples * 13]PointAffine
|
||||
for i := 0; i < 13; i++ {
|
||||
copy(samplePointsLarge[i*nbSamples:], samplePoints[:])
|
||||
}
|
||||
|
||||
var r16, splitted1, splitted2 PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples * 13]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars16, _ := partitionScalars(sampleScalars[:], 16, false, runtime.NumCPU())
|
||||
msmC16(&r16, samplePoints[:], scalars16, true)
|
||||
|
||||
MultiExp(&splitted1, samplePointsLarge[:], sampleScalars[:], MultiExpConfig{NbTasks: 128})
|
||||
MultiExp(&splitted2, samplePointsLarge[:], sampleScalars[:], MultiExpConfig{NbTasks: 51})
|
||||
return r16.Equal(&splitted1) && r16.Equal(&splitted2)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
if false {
|
||||
properties.Property("[G1] Multi exponentation (c=5, c=16) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var expected PointProj
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars5, _ := partitionScalars(sampleScalars[:], 5, false, runtime.NumCPU())
|
||||
scalars16, _ := partitionScalars(sampleScalars[:], 16, false, runtime.NumCPU())
|
||||
|
||||
var r5, r16 PointProj
|
||||
msmC5(&r5, samplePoints[:], scalars5, false)
|
||||
msmC16(&r16, samplePoints[:], scalars16, true)
|
||||
return (r5.Equal(&expected) && r16.Equal(&expected))
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
} else {
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=4) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 4, false, runtime.NumCPU())
|
||||
msmC4(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=5) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 5, false, runtime.NumCPU())
|
||||
msmC5(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=6) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 6, false, runtime.NumCPU())
|
||||
msmC6(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=7) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 7, false, runtime.NumCPU())
|
||||
msmC7(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=8) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 8, false, runtime.NumCPU())
|
||||
msmC8(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=9) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 9, false, runtime.NumCPU())
|
||||
msmC9(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=10) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 10, false, runtime.NumCPU())
|
||||
msmC10(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=11) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 11, false, runtime.NumCPU())
|
||||
msmC11(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=12) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 12, false, runtime.NumCPU())
|
||||
msmC12(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=13) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 13, false, runtime.NumCPU())
|
||||
msmC13(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=14) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 14, false, runtime.NumCPU())
|
||||
msmC14(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=15) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 15, false, runtime.NumCPU())
|
||||
msmC15(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=16) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 16, false, runtime.NumCPU())
|
||||
msmC16(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=20) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 20, false, runtime.NumCPU())
|
||||
msmC20(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=21) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 21, false, runtime.NumCPU())
|
||||
msmC21(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.Property("[G1] Multi exponentation (c=22) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var result, expected PointProj
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
}
|
||||
|
||||
scalars, _ := partitionScalars(sampleScalars[:], 22, false, runtime.NumCPU())
|
||||
msmC22(&result, samplePoints[:], scalars, false)
|
||||
|
||||
// compute expected result with double and add
|
||||
var finalScalar, mixerBigInt big.Int
|
||||
finalScalar.Mul(&scalar, mixer.ToBigIntRegular(&mixerBigInt))
|
||||
expected.ScalarMultiplication(&Generator, &finalScalar)
|
||||
|
||||
return result.Equal(&expected)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
// note : this test is here as we expect to have a different multiExp than the above bucket method
|
||||
// for small number of points
|
||||
properties.Property("[G1] Multi exponentation (<50points) should be consistant with sum of square", prop.ForAll(
|
||||
func(mixer fr.Element) bool {
|
||||
|
||||
var g PointProj
|
||||
g.Set(&Generator)
|
||||
|
||||
var GeneratorAff PointAffine
|
||||
GeneratorAff.FromProj(&Generator)
|
||||
|
||||
// mixer ensures that all the words of a fpElement are set
|
||||
samplePoints := make([]PointAffine, 30)
|
||||
sampleScalars := make([]fr.Element, 30)
|
||||
|
||||
for i := 1; i <= 30; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
samplePoints[i-1].FromProj(&g)
|
||||
g.Add(&g, &Generator)
|
||||
}
|
||||
|
||||
op1MultiExp, _ := MultiExpAffine(samplePoints, sampleScalars, MultiExpConfig{})
|
||||
|
||||
var finalBigScalar fr.Element
|
||||
var finalBigScalarBi big.Int
|
||||
var op1ScalarMul PointAffine
|
||||
finalBigScalar.SetString("9455").Mul(&finalBigScalar, &mixer)
|
||||
finalBigScalar.ToBigIntRegular(&finalBigScalarBi)
|
||||
op1ScalarMul.ScalarMultiplication(&GeneratorAff, &finalBigScalarBi)
|
||||
|
||||
return op1ScalarMul.Equal(&op1MultiExp)
|
||||
},
|
||||
genScalar,
|
||||
))
|
||||
|
||||
properties.TestingRun(t, gopter.ConsoleReporter(false))
|
||||
}
|
||||
|
||||
func BenchmarkMultiExpG1(b *testing.B) {
|
||||
|
||||
var GeneratorAff = GetEdwardsCurve().Base
|
||||
// ensure every words of the scalars are filled
|
||||
var mixer fr.Element
|
||||
mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487")
|
||||
|
||||
const pow = (bits.UintSize / 2) - (bits.UintSize / 8) // 24 on 64 bits arch, 12 on 32 bits
|
||||
const nbSamples = 1 << pow
|
||||
|
||||
var samplePoints [nbSamples]PointAffine
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
samplePoints[i-1] = GeneratorAff
|
||||
}
|
||||
|
||||
for i := 5; i <= pow; i++ {
|
||||
using := 1 << i
|
||||
|
||||
b.Run(fmt.Sprintf("%d points", using), func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for j := 0; j < b.N; j++ {
|
||||
_, _ = MultiExpAffine(samplePoints[:using], sampleScalars[:using], MultiExpConfig{})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMultiExpG1Reference(b *testing.B) {
|
||||
|
||||
var GeneratorAff = GetEdwardsCurve().Base
|
||||
|
||||
// ensure every words of the scalars are filled
|
||||
var mixer fr.Element
|
||||
mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487")
|
||||
|
||||
const nbSamples = 1 << 20
|
||||
|
||||
var samplePoints [nbSamples]PointAffine
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
samplePoints[i-1] = GeneratorAff
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for j := 0; j < b.N; j++ {
|
||||
_, _ = MultiExpAffine(samplePoints[:], sampleScalars[:], MultiExpConfig{})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkManyMultiExpG1Reference(b *testing.B) {
|
||||
|
||||
var GeneratorAff = GetEdwardsCurve().Base
|
||||
|
||||
// ensure every words of the scalars are filled
|
||||
var mixer fr.Element
|
||||
mixer.SetString("7716837800905789770901243404444209691916730933998574719964609384059111546487")
|
||||
|
||||
const nbSamples = 1 << 20
|
||||
|
||||
var samplePoints [nbSamples]PointAffine
|
||||
var sampleScalars [nbSamples]fr.Element
|
||||
|
||||
for i := 1; i <= nbSamples; i++ {
|
||||
sampleScalars[i-1].SetUint64(uint64(i)).
|
||||
Mul(&sampleScalars[i-1], &mixer).
|
||||
FromMont()
|
||||
samplePoints[i-1] = GeneratorAff
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for j := 0; j < b.N; j++ {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(3)
|
||||
go func() {
|
||||
_, _ = MultiExpAffine(samplePoints[:], sampleScalars[:], MultiExpConfig{})
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
_, _ = MultiExpAffine(samplePoints[:], sampleScalars[:], MultiExpConfig{})
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
_, _ = MultiExpAffine(samplePoints[:], sampleScalars[:], MultiExpConfig{})
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func GetEdwardsCurve() gnarkbandersnatch.CurveParams {
|
||||
return CurveParams
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
|
||||
|
||||
package multiproof
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/ipa"
|
||||
)
|
||||
|
||||
// ErrBatchLengthMismatch is returned when CheckMultiProofBatch receives
|
||||
// inputs whose outer slices disagree on length.
|
||||
var ErrBatchLengthMismatch = errors.New("ipa: batch slice length mismatch")
|
||||
|
||||
// BatchProof bundles a single multiproof with the public data needed to
|
||||
// check it. A BatchProofs slice is the canonical input shape for the
|
||||
// batched verifier (and for the GPU driver once it lands).
|
||||
type BatchProof struct {
|
||||
Proof *MultiProof // the multi-proof to verify
|
||||
Cs []*banderwagon.Element // commitments (per opening within this proof)
|
||||
Ys []*fr.Element // claimed evaluations
|
||||
Zs []uint8 // evaluation points (within domain)
|
||||
}
|
||||
|
||||
// CheckMultiProofBatch verifies many MultiProofs in lockstep. Each proof gets
|
||||
// its own fresh transcript (the protocol is "verifiable, deterministically
|
||||
// random per proof"); the batched form simply spares the caller a loop and
|
||||
// gives the C++ / GPU layer a contiguous workload to dispatch on.
|
||||
//
|
||||
// Returns nil on full success or an error on the first mismatch (with the
|
||||
// failing index embedded). Returning ErrBatchLengthMismatch means the input
|
||||
// is malformed; any other non-nil return means a specific proof failed.
|
||||
//
|
||||
// Future work: a fully-batched verifier that randomizes the linear combination
|
||||
// across proofs would amortize MSM cost; that's a separate code path.
|
||||
func CheckMultiProofBatch(transcriptLabel []byte, ipaConf *ipa.IPAConfig, proofs []BatchProof) error {
|
||||
if len(proofs) == 0 {
|
||||
return ErrBatchLengthMismatch
|
||||
}
|
||||
for i, bp := range proofs {
|
||||
if bp.Proof == nil {
|
||||
return fmt.Errorf("ipa batch[%d]: nil proof", i)
|
||||
}
|
||||
if len(bp.Cs) != len(bp.Ys) || len(bp.Cs) != len(bp.Zs) {
|
||||
return fmt.Errorf("ipa batch[%d]: %w", i, ErrBatchLengthMismatch)
|
||||
}
|
||||
t := common.NewTranscript(string(transcriptLabel))
|
||||
ok, err := CheckMultiProof(t, ipaConf, bp.Proof, bp.Cs, bp.Ys, bp.Zs)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ipa batch[%d]: %w", i, err)
|
||||
}
|
||||
if !ok {
|
||||
return fmt.Errorf("ipa batch[%d]: proof failed", i)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
|
||||
|
||||
package multiproof
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/ipa"
|
||||
"github.com/luxfi/crypto/ipa/test_helper"
|
||||
)
|
||||
|
||||
// makeBatchProof produces a single BatchProof for testing using a known
|
||||
// polynomial/evaluation point pair. Each call uses a fresh transcript so
|
||||
// the proof bytes match what a non-batched caller would produce.
|
||||
func makeBatchProof(t *testing.T, conf *ipa.IPAConfig, seed uint64, z uint8) BatchProof {
|
||||
t.Helper()
|
||||
// Build a deterministic poly from seed.
|
||||
var coeffs []uint64
|
||||
for i := uint64(0); i < 16; i++ {
|
||||
coeffs = append(coeffs, seed+i)
|
||||
}
|
||||
poly := test_helper.TestPoly256(coeffs...)
|
||||
C := conf.Commit(poly)
|
||||
tr := common.NewTranscript("batch-kat")
|
||||
mp, err := CreateMultiProof(tr, conf, []*banderwagon.Element{&C}, [][]fr.Element{poly}, []uint8{z})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
y := poly[z]
|
||||
return BatchProof{
|
||||
Proof: mp,
|
||||
Cs: []*banderwagon.Element{&C},
|
||||
Ys: []*fr.Element{&y},
|
||||
Zs: []uint8{z},
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckMultiProofBatch_KAT runs 10 batched proofs through the verifier
|
||||
// and asserts each one succeeds. The matrix of (seed, z) covers a healthy
|
||||
// range of polynomial domains and evaluation points.
|
||||
func TestCheckMultiProofBatch_KAT(t *testing.T) {
|
||||
conf, err := ipa.NewIPASettings()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cases := []struct {
|
||||
seed uint64
|
||||
z uint8
|
||||
}{
|
||||
{1, 0},
|
||||
{2, 1},
|
||||
{3, 2},
|
||||
{4, 7},
|
||||
{5, 31},
|
||||
{6, 63},
|
||||
{7, 127},
|
||||
{8, 200},
|
||||
{9, 254},
|
||||
{10, 255},
|
||||
}
|
||||
proofs := make([]BatchProof, len(cases))
|
||||
for i, tc := range cases {
|
||||
proofs[i] = makeBatchProof(t, conf, tc.seed, tc.z)
|
||||
}
|
||||
if err := CheckMultiProofBatch([]byte("batch-kat"), conf, proofs); err != nil {
|
||||
t.Fatalf("batch verify failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMultiProofBatch_TamperDetected(t *testing.T) {
|
||||
conf, err := ipa.NewIPASettings()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bp := makeBatchProof(t, conf, 1, 0)
|
||||
// Tamper with the y value.
|
||||
var bogus fr.Element
|
||||
bogus.SetUint64(0xDEADBEEF)
|
||||
bp.Ys = []*fr.Element{&bogus}
|
||||
if err := CheckMultiProofBatch([]byte("batch-kat"), conf, []BatchProof{bp}); err == nil {
|
||||
t.Fatal("expected error from tampered proof")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckMultiProofBatch_LengthMismatch(t *testing.T) {
|
||||
conf, err := ipa.NewIPASettings()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := CheckMultiProofBatch([]byte("x"), conf, nil); err != ErrBatchLengthMismatch {
|
||||
t.Fatalf("expected ErrBatchLengthMismatch on empty input, got %v", err)
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
)
|
||||
|
||||
// VectorLength is the number of elements in the vector. This value is fixed.
|
||||
// Note that this means that the degree of the polynomial is one less than this value.
|
||||
const VectorLength = 256
|
||||
|
||||
// PowersOf returns powers of x from 0 to degree-1: <1, x, x^2, ..., x^(degree-1)>.
|
||||
// Used for polynomial evaluation and computing challenge powers in IPA proofs.
|
||||
func PowersOf(x fr.Element, degree int) []fr.Element {
|
||||
result := make([]fr.Element, degree)
|
||||
result[0] = fr.One()
|
||||
|
||||
for i := 1; i < degree; i++ {
|
||||
result[i].Mul(&result[i-1], &x)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func ReadPoint(r io.Reader) (*banderwagon.Element, error) {
|
||||
var x = make([]byte, 32)
|
||||
if _, err := io.ReadAtLeast(r, x, 32); err != nil {
|
||||
return nil, fmt.Errorf("reading x coordinate: %w", err)
|
||||
}
|
||||
var p = &banderwagon.Element{}
|
||||
if err := p.SetBytes(x); err != nil {
|
||||
return nil, fmt.Errorf("deserializing point: %w", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func ReadScalar(r io.Reader) (*fr.Element, error) {
|
||||
var x = make([]byte, 32)
|
||||
if _, err := io.ReadAtLeast(r, x, 32); err != nil {
|
||||
return nil, fmt.Errorf("reading scalar: %w", err)
|
||||
}
|
||||
var scalar = &fr.Element{}
|
||||
if _, err := scalar.SetBytesLECanonical(x); err != nil {
|
||||
return nil, fmt.Errorf("deserializing scalar: %s", err)
|
||||
}
|
||||
|
||||
return scalar, nil
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// 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 parallel
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Execute process in parallel the work function
|
||||
func Execute(nbIterations int, work func(int, int), maxCpus ...int) {
|
||||
|
||||
nbTasks := runtime.NumCPU()
|
||||
if len(maxCpus) == 1 {
|
||||
nbTasks = maxCpus[0]
|
||||
}
|
||||
nbIterationsPerCpus := nbIterations / nbTasks
|
||||
|
||||
// more CPUs than tasks: a CPU will work on exactly one iteration
|
||||
if nbIterationsPerCpus < 1 {
|
||||
nbIterationsPerCpus = 1
|
||||
nbTasks = nbIterations
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
extraTasks := nbIterations - (nbTasks * nbIterationsPerCpus)
|
||||
extraTasksOffset := 0
|
||||
|
||||
for i := 0; i < nbTasks; i++ {
|
||||
wg.Add(1)
|
||||
_start := i*nbIterationsPerCpus + extraTasksOffset
|
||||
_end := _start + nbIterationsPerCpus
|
||||
if extraTasks > 0 {
|
||||
_end++
|
||||
extraTasks--
|
||||
extraTasksOffset++
|
||||
}
|
||||
go func() {
|
||||
work(_start, _end)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"hash"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
)
|
||||
|
||||
// The transcript is used to create challenge scalars.
|
||||
// See: Fiat-Shamir
|
||||
type Transcript struct {
|
||||
state hash.Hash
|
||||
buff *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewTranscript(label string) *Transcript {
|
||||
digest := sha256.New()
|
||||
digest.Write([]byte(label))
|
||||
|
||||
transcript := &Transcript{
|
||||
state: digest,
|
||||
buff: bytes.NewBuffer(make([]byte, 0, 1024)),
|
||||
}
|
||||
|
||||
return transcript
|
||||
}
|
||||
|
||||
func (t *Transcript) AppendMessage(message []byte, label []byte) {
|
||||
t.buff.Write(label)
|
||||
t.buff.Write(message)
|
||||
}
|
||||
|
||||
// Appends a Scalar to the transcript
|
||||
//
|
||||
// Converts the scalar to 32 bytes, then appends it to
|
||||
// the state
|
||||
func (t *Transcript) AppendScalar(scalar *fr.Element, label []byte) {
|
||||
tmpBytes := scalar.BytesLE()
|
||||
t.AppendMessage(tmpBytes[:], label)
|
||||
|
||||
}
|
||||
|
||||
// Appends a Point to the transcript
|
||||
//
|
||||
// Compresses the Point into a 32 byte slice, then appends it to
|
||||
// the state
|
||||
func (t *Transcript) AppendPoint(point *banderwagon.Element, label []byte) {
|
||||
tmp_bytes := point.Bytes()
|
||||
t.AppendMessage(tmp_bytes[:], label)
|
||||
|
||||
}
|
||||
|
||||
func (t *Transcript) DomainSep(label []byte) {
|
||||
t.buff.Write(label)
|
||||
}
|
||||
|
||||
// Computes a challenge based off of the state of the transcript
|
||||
//
|
||||
// Hash the transcript state, then reduce the hash modulo the size of the
|
||||
// scalar field
|
||||
//
|
||||
// Note that calling the transcript twice, will yield two different challenges
|
||||
func (t *Transcript) ChallengeScalar(label []byte) fr.Element {
|
||||
t.DomainSep(label)
|
||||
|
||||
t.state.Write(t.buff.Bytes())
|
||||
t.buff.Reset()
|
||||
// Reverse the endian so we are using little-endian
|
||||
// SetBytes interprets the bytes in Big Endian
|
||||
bytes := t.state.Sum(nil)
|
||||
|
||||
var tmp fr.Element
|
||||
tmp.SetBytesLE(bytes)
|
||||
|
||||
// Clear the state
|
||||
t.state.Reset()
|
||||
|
||||
// Add the new challenge to the state
|
||||
// Which "summarises" the previous state before we cleared it
|
||||
t.AppendScalar(&tmp, label)
|
||||
|
||||
// Return the new challenge
|
||||
return tmp
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
)
|
||||
|
||||
func TestVector0(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tr := NewTranscript("simple_protocol")
|
||||
challenge_1 := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
challenge_2 := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
|
||||
if challenge_1 == challenge_2 {
|
||||
t.Fatal("calling ChallengeScalar twice should yield two different challenges")
|
||||
}
|
||||
}
|
||||
func TestVector1(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tr := NewTranscript("simple_protocol")
|
||||
challenge := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
c_bytes := challenge.BytesLE()
|
||||
|
||||
expected := "c2aa02607cbdf5595f00ee0dd94a2bbff0bed6a2bf8452ada9011eadb538d003"
|
||||
got := hex.EncodeToString(c_bytes[:])
|
||||
if expected != got {
|
||||
t.Fatal("computed challenge scalar is incorrect")
|
||||
}
|
||||
}
|
||||
func TestVector2(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tr := NewTranscript("simple_protocol")
|
||||
five := fr.Element{}
|
||||
five.SetUint64(5)
|
||||
|
||||
tr.AppendScalar(&five, []byte("five"))
|
||||
tr.AppendScalar(&five, []byte("five again"))
|
||||
|
||||
challenge := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
c_bytes := challenge.BytesLE()
|
||||
|
||||
expected := "498732b694a8ae1622d4a9347535be589e4aee6999ffc0181d13fe9e4d037b0b"
|
||||
got := hex.EncodeToString(c_bytes[:])
|
||||
if expected != got {
|
||||
t.Fatal("computed challenge scalar is incorrect")
|
||||
}
|
||||
}
|
||||
func TestVector3(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tr := NewTranscript("simple_protocol")
|
||||
one := fr.One()
|
||||
minus_one := fr.MinusOne()
|
||||
|
||||
tr.AppendScalar(&minus_one, []byte("-1"))
|
||||
tr.DomainSep([]byte("separate me"))
|
||||
tr.AppendScalar(&minus_one, []byte("-1 again"))
|
||||
tr.DomainSep([]byte("separate me again"))
|
||||
tr.AppendScalar(&one, []byte("now 1"))
|
||||
|
||||
challenge := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
c_bytes := challenge.BytesLE()
|
||||
|
||||
expected := "14f59938e9e9b1389e74311a464f45d3d88d8ac96adf1c1129ac466de088d618"
|
||||
got := hex.EncodeToString(c_bytes[:])
|
||||
if expected != got {
|
||||
t.Fatal("computed challenge scalar is incorrect")
|
||||
}
|
||||
}
|
||||
func TestVector4(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tr := NewTranscript("simple_protocol")
|
||||
|
||||
gen := banderwagon.Generator
|
||||
tr.AppendPoint(&gen, []byte("generator"))
|
||||
|
||||
challenge := tr.ChallengeScalar([]byte("simple_challenge"))
|
||||
c_bytes := challenge.BytesLE()
|
||||
|
||||
expected := "8c2dafe7c0aabfa9ed542bb2cbf0568399ae794fc44fdfd7dff6cc0e6144921c"
|
||||
got := hex.EncodeToString(c_bytes[:])
|
||||
if expected != got {
|
||||
t.Fatal("computed challenge scalar is incorrect")
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
)
|
||||
|
||||
// domainSize will always equal 256, which is the same
|
||||
// as the degree of the polynomial (+1), we are committing to.
|
||||
// This constant is defined here for semantic reasons.
|
||||
const domainSize = common.VectorLength
|
||||
|
||||
// PrecomputedWeights contains precomputed coefficients for efficient
|
||||
// usage of the Barycentric formula.
|
||||
type PrecomputedWeights struct {
|
||||
// This stores A'(x_i) and 1/A'(x_i)
|
||||
barycentricWeights []fr.Element
|
||||
// This stores 1/k and -1/k for k \in [0, 255]
|
||||
invertedDomain []fr.Element
|
||||
}
|
||||
|
||||
// NewPrecomputedWeights generates the precomputed weights for the barycentric formula.
|
||||
func NewPrecomputedWeights() *PrecomputedWeights {
|
||||
// Imagine we have two arrays of the same length and we concatenate them together
|
||||
// This is how we will store the A'(x_i) and 1/A'(x_i)
|
||||
// This midpoint variable is used to compute the offset that we need
|
||||
// to place 1/A'(x_i)
|
||||
midpoint := uint64(domainSize)
|
||||
|
||||
// Note there are DOMAIN_SIZE number of weights, but we are also storing their inverses
|
||||
// so we need double the amount of space
|
||||
barycentricWeights := make([]fr.Element, midpoint*2)
|
||||
for i := uint64(0); i < midpoint; i++ {
|
||||
weight := computeBarycentricWeightForElement(i)
|
||||
|
||||
var invWeight fr.Element
|
||||
invWeight.Inverse(&weight)
|
||||
|
||||
barycentricWeights[i] = weight
|
||||
barycentricWeights[i+midpoint] = invWeight
|
||||
}
|
||||
|
||||
// Computing 1/k and -1/k for k \in [0, 255]
|
||||
// Note that since we cannot do 1/0, we have one less element
|
||||
midpoint = domainSize - 1
|
||||
invertedDomain := make([]fr.Element, midpoint*2)
|
||||
for i := uint64(1); i < domainSize; i++ {
|
||||
var k fr.Element
|
||||
k.SetUint64(i)
|
||||
k.Inverse(&k)
|
||||
|
||||
var negative_k fr.Element
|
||||
zero := fr.Zero()
|
||||
negative_k.Sub(&zero, &k)
|
||||
|
||||
invertedDomain[i-1] = k
|
||||
invertedDomain[(i-1)+midpoint] = negative_k
|
||||
}
|
||||
|
||||
return &PrecomputedWeights{
|
||||
barycentricWeights: barycentricWeights,
|
||||
invertedDomain: invertedDomain,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// computes A'(x_j) where x_j must be an element in the domain
|
||||
// This is computed as the product of x_j - x_i where x_i is an element in the domain
|
||||
// and x_i is not equal to x_j
|
||||
func computeBarycentricWeightForElement(element uint64) fr.Element {
|
||||
// let domain_element_fr = Fr::from(domain_element as u128);
|
||||
if element > domainSize {
|
||||
panic(fmt.Sprintf("the domain is [0,255], %d is not in the domain", element))
|
||||
}
|
||||
|
||||
var domain_element_fr fr.Element
|
||||
domain_element_fr.SetUint64(element)
|
||||
|
||||
total := fr.One()
|
||||
|
||||
for i := uint64(0); i < domainSize; i++ {
|
||||
if i == element {
|
||||
continue
|
||||
}
|
||||
|
||||
var i_fr fr.Element
|
||||
i_fr.SetUint64(i)
|
||||
|
||||
var tmp fr.Element
|
||||
tmp.Sub(&domain_element_fr, &i_fr)
|
||||
|
||||
total.Mul(&total, &tmp)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
// ComputeBarycentricCoefficients, computes the coefficients `bary_coeffs`
|
||||
// for a point `z` such that when we have a polynomial `p` in lagrange
|
||||
// basis, the inner product of `p` and `bary_coeffs` is equal to p(z)
|
||||
// Note that `z` should not be in the domain.
|
||||
// This can also be seen as the lagrange coefficients L_i(point)
|
||||
func (preComp *PrecomputedWeights) ComputeBarycentricCoefficients(point fr.Element) []fr.Element {
|
||||
// Compute A'(x_i) * (point - x_i)
|
||||
lagrangeEvals := make([]fr.Element, domainSize)
|
||||
for i := uint64(0); i < domainSize; i++ {
|
||||
weight := preComp.barycentricWeights[i]
|
||||
|
||||
var i_fr fr.Element
|
||||
i_fr.SetUint64(i)
|
||||
lagrangeEvals[i].Sub(&point, &i_fr)
|
||||
lagrangeEvals[i].Mul(&lagrangeEvals[i], &weight)
|
||||
}
|
||||
|
||||
totalProd := fr.One()
|
||||
for i := uint64(0); i < domainSize; i++ {
|
||||
var i_fr fr.Element
|
||||
i_fr.SetUint64(i)
|
||||
|
||||
var tmp fr.Element
|
||||
tmp.Sub(&point, &i_fr)
|
||||
totalProd.Mul(&totalProd, &tmp)
|
||||
}
|
||||
|
||||
lagrangeEvals = fr.BatchInvert(lagrangeEvals)
|
||||
for i := uint64(0); i < domainSize; i++ {
|
||||
lagrangeEvals[i].Mul(&lagrangeEvals[i], &totalProd)
|
||||
}
|
||||
|
||||
return lagrangeEvals
|
||||
}
|
||||
|
||||
// DivideOnDomain computes f(x) - f(x_i) / x - x_i where x_i is an element in the domain
|
||||
func (preComp *PrecomputedWeights) DivideOnDomain(index uint8, f []fr.Element) []fr.Element {
|
||||
quotient := make([]fr.Element, domainSize)
|
||||
|
||||
y := f[index]
|
||||
|
||||
for i := 0; i < domainSize; i++ {
|
||||
if i != int(index) {
|
||||
den := i - int(index)
|
||||
absDen, is_neg := absInt(den)
|
||||
|
||||
denInv := preComp.getInvertedElement(absDen, is_neg)
|
||||
|
||||
// compute q_i
|
||||
quotient[i].Sub(&f[i], &y)
|
||||
quotient[i].Mul("ient[i], &denInv)
|
||||
|
||||
weightRatio := preComp.getRatioOfWeights(int(index), i)
|
||||
var tmp fr.Element
|
||||
tmp.Mul(&weightRatio, "ient[i])
|
||||
quotient[index].Sub("ient[index], &tmp)
|
||||
}
|
||||
}
|
||||
|
||||
return quotient
|
||||
}
|
||||
|
||||
func (preComp *PrecomputedWeights) getInvertedElement(element int, is_neg bool) fr.Element {
|
||||
index := element - 1
|
||||
|
||||
if is_neg {
|
||||
midpoint := len(preComp.invertedDomain) / 2
|
||||
index += midpoint
|
||||
}
|
||||
|
||||
return preComp.invertedDomain[index]
|
||||
}
|
||||
|
||||
func (preComp *PrecomputedWeights) getRatioOfWeights(numerator int, denominator int) fr.Element {
|
||||
|
||||
a := preComp.barycentricWeights[numerator]
|
||||
midpoint := len(preComp.barycentricWeights) / 2
|
||||
b := preComp.barycentricWeights[denominator+midpoint]
|
||||
|
||||
var result fr.Element
|
||||
result.Mul(&a, &b)
|
||||
return result
|
||||
}
|
||||
|
||||
func (preComp *PrecomputedWeights) getInverseBarycentricWeight(i int) fr.Element {
|
||||
|
||||
midpoint := len(preComp.barycentricWeights) / 2
|
||||
return preComp.barycentricWeights[i+midpoint]
|
||||
}
|
||||
|
||||
// Returns the absolute value and true if
|
||||
// the value was negative
|
||||
func absInt(x int) (int, bool) {
|
||||
is_negative := x < 0
|
||||
|
||||
if is_negative {
|
||||
return -x, is_negative
|
||||
}
|
||||
|
||||
return x, is_negative
|
||||
}
|
||||
@@ -1,445 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/test_helper"
|
||||
)
|
||||
|
||||
func TestAbsInt(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
abs, is_neg := absInt(-100)
|
||||
if abs != 100 {
|
||||
t.Fatal("absolute value should be 100")
|
||||
}
|
||||
if !is_neg {
|
||||
t.Fatal("input value was negative")
|
||||
}
|
||||
|
||||
abs, is_neg = absInt(250)
|
||||
if abs != 250 {
|
||||
t.Fatal("absolute value should be 250")
|
||||
}
|
||||
if is_neg {
|
||||
t.Fatal("input value was positive")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The interpolation is only needed for tests,
|
||||
// but we need to make sure it is correct.
|
||||
// abstractly, you can think of it as getting the
|
||||
// associated polynomial in coefficient form
|
||||
// for a bunch of points
|
||||
func TestBasicInterpolate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// These two points define the polynomial y = X
|
||||
// Once we interpolate the polynomial, any point
|
||||
// we evalate the polynomial at, should return the point
|
||||
point_a := Point{
|
||||
x: fr.Zero(),
|
||||
y: fr.Zero(),
|
||||
}
|
||||
point_b := Point{
|
||||
x: fr.One(),
|
||||
y: fr.One(),
|
||||
}
|
||||
points := Points{point_a, point_b}
|
||||
poly := points.interpolate(t)
|
||||
|
||||
var rand_fr fr.Element
|
||||
_, err := rand_fr.SetRandom()
|
||||
if err != nil {
|
||||
t.Fatal("could not generate a random element")
|
||||
}
|
||||
result := poly.evaluate(rand_fr)
|
||||
|
||||
if !result.Equal(&rand_fr) {
|
||||
t.Fatal("result should be rand_fr, because the polynomial should be the identity polynomial")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPolyDiv(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
one := fr.One()
|
||||
minus_one := fr.MinusOne()
|
||||
|
||||
var minus_two fr.Element
|
||||
minus_two.Sub(&minus_one, &one)
|
||||
|
||||
var minus_three fr.Element
|
||||
minus_three.Sub(&minus_two, &one)
|
||||
|
||||
var two fr.Element
|
||||
two.SetUint64(2)
|
||||
|
||||
// (X-1)(X-2) = 2 - 3X + X^2
|
||||
poly_coeff_numerator := []fr.Element{two, minus_three, one}
|
||||
|
||||
// - 1 + X
|
||||
poly_coeff_denominator := []fr.Element{minus_one, one}
|
||||
quotient, rem, ok := pld(poly_coeff_numerator, poly_coeff_denominator)
|
||||
if !ok {
|
||||
t.Fatal("poly div failed")
|
||||
}
|
||||
|
||||
for _, x := range rem {
|
||||
if !x.IsZero() {
|
||||
fmt.Printf("%v", x)
|
||||
t.Fatal("remainder should be zero")
|
||||
}
|
||||
}
|
||||
|
||||
// The quotient should be X - 2, lets evaluate it and check this is correct
|
||||
var rand_fr fr.Element
|
||||
_, err := rand_fr.SetRandom()
|
||||
if err != nil {
|
||||
t.Fatal("could not get randomness")
|
||||
}
|
||||
got := Poly(quotient).evaluate(rand_fr)
|
||||
|
||||
var expected fr.Element
|
||||
expected.Add(&rand_fr, &minus_two)
|
||||
|
||||
if !expected.Equal(&got) {
|
||||
t.Fatal("quotient is not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeBarycentricCoefficients(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var point_outside_domain fr.Element
|
||||
point_outside_domain.SetUint64(3400)
|
||||
|
||||
lagrange_values := test_helper.TestPoly256(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||
|
||||
preComp := NewPrecomputedWeights()
|
||||
bar_coeffs := preComp.ComputeBarycentricCoefficients(point_outside_domain)
|
||||
got, err := InnerProd(lagrange_values, bar_coeffs)
|
||||
if err != nil {
|
||||
t.Fatalf("inner product failed: %v", err)
|
||||
}
|
||||
expected := evalOutsideDomain(preComp, lagrange_values, point_outside_domain)
|
||||
|
||||
points := Points{}
|
||||
for k := 0; k < 256; k++ {
|
||||
var x fr.Element
|
||||
x.SetUint64(uint64(k))
|
||||
|
||||
point := Point{
|
||||
x: x,
|
||||
y: lagrange_values[k],
|
||||
}
|
||||
points = append(points, point)
|
||||
}
|
||||
poly_coeff := points.interpolate(t)
|
||||
expected2 := poly_coeff.evaluate(point_outside_domain)
|
||||
|
||||
if !expected2.Equal(&expected) {
|
||||
t.Fatal("problem with barycentric weights")
|
||||
}
|
||||
|
||||
if !expected2.Equal(&got) {
|
||||
t.Fatal("problem with inner product")
|
||||
}
|
||||
}
|
||||
|
||||
// evalOutsideDomain evaluates a polynomial at a point outside the precomputed domain.
|
||||
// Kept as a test helper; production code uses PrecomputedWeights directly.
|
||||
func evalOutsideDomain(preComp *PrecomputedWeights, f []fr.Element, point fr.Element) fr.Element {
|
||||
|
||||
pointMinusDomain := make([]fr.Element, domainSize)
|
||||
for i := 0; i < domainSize; i++ {
|
||||
|
||||
var i_fr fr.Element
|
||||
i_fr.SetUint64(uint64(i))
|
||||
pointMinusDomain[i].Sub(&point, &i_fr)
|
||||
pointMinusDomain[i].Inverse(&pointMinusDomain[i])
|
||||
}
|
||||
|
||||
summand := fr.Zero()
|
||||
for x_i := 0; x_i < len(pointMinusDomain); x_i++ {
|
||||
weight := preComp.getInverseBarycentricWeight(x_i)
|
||||
var term fr.Element
|
||||
term.Mul(&weight, &f[x_i])
|
||||
term.Mul(&term, &pointMinusDomain[x_i])
|
||||
summand.Add(&summand, &term)
|
||||
}
|
||||
|
||||
a_z := fr.One()
|
||||
for i := 0; i < domainSize; i++ {
|
||||
|
||||
var i_fr fr.Element
|
||||
i_fr.SetUint64(uint64(i))
|
||||
|
||||
var tmp fr.Element
|
||||
tmp.Sub(&point, &i_fr)
|
||||
a_z.Mul(&a_z, &tmp)
|
||||
}
|
||||
a_z.Mul(&a_z, &summand)
|
||||
|
||||
return a_z
|
||||
}
|
||||
|
||||
func TestDivideOnDomain(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// First lets define the polynomial (X-1)(X+1)(X)^253
|
||||
eval_f := func(x fr.Element) fr.Element {
|
||||
// f is (X-1)(X+1)(X^253)
|
||||
var tmp_a fr.Element
|
||||
one := fr.One()
|
||||
tmp_a.Sub(&x, &one)
|
||||
|
||||
var tmp_b fr.Element
|
||||
tmp_b.Add(&x, &one)
|
||||
|
||||
tmp_c := one
|
||||
for i := 0; i < 253; i++ {
|
||||
tmp_c.Mul(&tmp_c, &x)
|
||||
}
|
||||
|
||||
var res fr.Element
|
||||
res.Mul(&tmp_a, &tmp_b)
|
||||
res.Mul(&res, &tmp_c)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
points := Points{}
|
||||
for k := 0; k < 256; k++ {
|
||||
var x fr.Element
|
||||
x.SetUint64(uint64(k))
|
||||
|
||||
point := Point{
|
||||
x: x,
|
||||
y: eval_f(x),
|
||||
}
|
||||
points = append(points, point)
|
||||
}
|
||||
|
||||
numerator_poly_coeff := points.interpolate(t)
|
||||
|
||||
// X - 1 (This is chosen because we know it divides perfectly into the numerator)
|
||||
denom_poly_coeff := Poly{fr.MinusOne(), fr.One()}
|
||||
|
||||
preComp := NewPrecomputedWeights()
|
||||
index := uint8(1) // One, because this is the same as dividing by X - 1, note that at x=1, we have a root
|
||||
|
||||
// We need just the `y` values from points (ie just the evaluations)
|
||||
evaluations := make([]fr.Element, 256)
|
||||
for i, p := range points {
|
||||
evaluations[i] = p.y
|
||||
}
|
||||
if !evaluations[index].IsZero() {
|
||||
t.Fatal("dividing on the domain with `index` will not have a remainder of zero")
|
||||
}
|
||||
quotientLag := preComp.DivideOnDomain(index, evaluations)
|
||||
|
||||
// Note quotientLag is the result of dividing the polynomial by X - 1, but in lagrange form
|
||||
// we should get the same result, if we do this is coefficient form
|
||||
|
||||
expected_quotient_coeff, rem, ok := pld(numerator_poly_coeff, denom_poly_coeff)
|
||||
if !ok {
|
||||
t.Fatal("polynomial division failed")
|
||||
}
|
||||
|
||||
// Remainder should be zero
|
||||
for _, r := range rem {
|
||||
if !r.IsZero() {
|
||||
t.Fatal("remainder should be zero")
|
||||
}
|
||||
}
|
||||
|
||||
// Lets check that the expected value is correct for good measure, we can do this by
|
||||
// checking it's roots, since we divided by X - 1, the new polynomial should be:
|
||||
// (X+1)(X^253)
|
||||
should_be_zero := Poly(expected_quotient_coeff).evaluate(fr.MinusOne())
|
||||
if !should_be_zero.IsZero() {
|
||||
t.Fatal("-1 is not a root, but it should be")
|
||||
}
|
||||
should_be_zero = Poly(expected_quotient_coeff).evaluate(fr.One())
|
||||
if should_be_zero.IsZero() {
|
||||
t.Fatal("1 is a root, but it should not be, because we just divided by X - 1")
|
||||
}
|
||||
should_be_zero = Poly(expected_quotient_coeff).evaluate(fr.Zero())
|
||||
if !should_be_zero.IsZero() {
|
||||
t.Fatal("0 is not a root, but it should be")
|
||||
}
|
||||
|
||||
// Lets convert quotientLag to coefficient form
|
||||
var quotientLagEvaluations Points
|
||||
for x, y := range quotientLag {
|
||||
var x_fr fr.Element
|
||||
x_fr.SetUint64(uint64(x))
|
||||
|
||||
point := Point{
|
||||
x: x_fr,
|
||||
y: y,
|
||||
}
|
||||
quotientLagEvaluations = append(quotientLagEvaluations, point)
|
||||
}
|
||||
got_quotient_coeff := quotientLagEvaluations.interpolate(t)
|
||||
|
||||
var rand_fr fr.Element
|
||||
_, err := rand_fr.SetRandom()
|
||||
if err != nil {
|
||||
t.Fatal("could not get randomness")
|
||||
}
|
||||
got_res := got_quotient_coeff.evaluate(rand_fr)
|
||||
expected_res := Poly(expected_quotient_coeff).evaluate(rand_fr)
|
||||
|
||||
if !expected_res.Equal(&got_res) {
|
||||
t.Fatal("polynomials are different")
|
||||
}
|
||||
|
||||
top_term := got_quotient_coeff[len(got_quotient_coeff)-1]
|
||||
if !top_term.IsZero() {
|
||||
t.Fatal("top term is not zero, degree is incorrect")
|
||||
}
|
||||
got_quotient_coeff = got_quotient_coeff[:len(got_quotient_coeff)-1]
|
||||
|
||||
if len(expected_quotient_coeff) != len(got_quotient_coeff) {
|
||||
t.Fatalf("expected quotiend coefficients %d != got quotiend coefficients %d", len(expected_quotient_coeff), len(got_quotient_coeff))
|
||||
}
|
||||
|
||||
for i := 0; i < len(expected_quotient_coeff); i++ {
|
||||
|
||||
if !got_quotient_coeff[i].Equal(&expected_quotient_coeff[i]) {
|
||||
t.Fatal("polynomials are not the same")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Point struct {
|
||||
x fr.Element
|
||||
y fr.Element
|
||||
}
|
||||
|
||||
type Points []Point
|
||||
|
||||
type Poly []fr.Element
|
||||
|
||||
func (poly Poly) evaluate(point fr.Element) fr.Element {
|
||||
powers := common.PowersOf(point, len(poly))
|
||||
total := fr.Zero()
|
||||
for i := 0; i < len(poly); i++ {
|
||||
var tmp fr.Element
|
||||
tmp.Mul(&powers[i], &poly[i])
|
||||
total.Add(&total, &tmp)
|
||||
}
|
||||
return total
|
||||
}
|
||||
func (points Points) interpolate(t *testing.T) Poly {
|
||||
one := fr.One()
|
||||
zero := fr.Zero()
|
||||
|
||||
max_degree_plus_one := len(points)
|
||||
if max_degree_plus_one < 2 {
|
||||
t.Fatal("should interpolate for degree >= 1")
|
||||
}
|
||||
coeffs := make([]fr.Element, max_degree_plus_one)
|
||||
|
||||
for k := 0; k < len(points); k++ {
|
||||
point := points[k]
|
||||
x_k := point.x
|
||||
y_k := point.y
|
||||
|
||||
contribution := make([]fr.Element, max_degree_plus_one)
|
||||
denominator := fr.One()
|
||||
max_contribution_degree := 0
|
||||
for j := 0; j < len(points); j++ {
|
||||
point := points[j]
|
||||
x_j := point.x
|
||||
if j == k {
|
||||
continue
|
||||
}
|
||||
|
||||
diff := x_k
|
||||
diff.Sub(&diff, &x_j)
|
||||
denominator.Mul(&denominator, &diff)
|
||||
if max_contribution_degree == 0 {
|
||||
|
||||
max_contribution_degree = 1
|
||||
contribution[0].Sub(&contribution[0], &x_j)
|
||||
contribution[1].Add(&contribution[1], &one)
|
||||
|
||||
} else {
|
||||
var mul_by_minus_x_j []fr.Element
|
||||
for _, el := range contribution {
|
||||
tmp := el
|
||||
tmp.Mul(&tmp, &x_j)
|
||||
tmp.Sub(&zero, &tmp)
|
||||
mul_by_minus_x_j = append(mul_by_minus_x_j, tmp)
|
||||
}
|
||||
contribution = append([]fr.Element{zero}, contribution...)
|
||||
contribution = truncate(contribution, max_degree_plus_one)
|
||||
if max_degree_plus_one != len(mul_by_minus_x_j) {
|
||||
t.Fatal("malformed mul_by_minus_x_j")
|
||||
}
|
||||
for i := 0; i < len(contribution); i++ {
|
||||
other := mul_by_minus_x_j[i]
|
||||
contribution[i].Add(&contribution[i], &other)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
denominator.Inverse(&denominator)
|
||||
if denominator.IsZero() {
|
||||
t.Fatal("denominator should not be zero")
|
||||
}
|
||||
for i := 0; i < len(contribution); i++ {
|
||||
tmp := contribution[i]
|
||||
tmp.Mul(&tmp, &denominator)
|
||||
tmp.Mul(&tmp, &y_k)
|
||||
coeffs[i].Add(&coeffs[i], &tmp)
|
||||
}
|
||||
|
||||
}
|
||||
return coeffs
|
||||
}
|
||||
|
||||
func truncate(s []fr.Element, to int) []fr.Element {
|
||||
return s[:to]
|
||||
}
|
||||
|
||||
func degree(p []fr.Element) int {
|
||||
for d := len(p) - 1; d >= 0; d-- {
|
||||
|
||||
if !p[d].IsZero() {
|
||||
return d
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Taken from https://rosettacode.org/wiki/Polynomial_long_division#Go
|
||||
func pld(nn, dd []fr.Element) (q, r []fr.Element, ok bool) {
|
||||
if degree(dd) < 0 {
|
||||
return
|
||||
}
|
||||
nn = append(r, nn...)
|
||||
if degree(nn) >= degree(dd) {
|
||||
q = make([]fr.Element, degree(nn)-degree(dd)+1)
|
||||
for degree(nn) >= degree(dd) {
|
||||
d := make([]fr.Element, degree(nn)+1)
|
||||
copy(d[degree(nn)-degree(dd):], dd)
|
||||
var tmp fr.Element
|
||||
tmp.Div(&nn[degree(nn)], &d[degree(d)])
|
||||
q[degree(nn)-degree(dd)] = tmp
|
||||
for i := range d {
|
||||
d[i].Mul(&d[i], &q[degree(nn)-degree(dd)])
|
||||
nn[i].Sub(&nn[i], &d[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
return q, nn, true
|
||||
}
|
||||
@@ -1,260 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fp"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
)
|
||||
|
||||
// IPAConfig contains all the necessary information to create an IPA related proofs,
|
||||
// such as the SRS, Q, and precomputed weights for the barycentric formula.
|
||||
type IPAConfig struct {
|
||||
SRS []banderwagon.Element
|
||||
Q banderwagon.Element
|
||||
|
||||
PrecompMSM banderwagon.MSMPrecomp
|
||||
PrecomputedWeights *PrecomputedWeights
|
||||
// The number of rounds the prover and verifier must complete
|
||||
// in the IPA argument, this will be log2 of the size of the input vectors
|
||||
// since the vector is halved on each round
|
||||
numRounds uint32
|
||||
}
|
||||
|
||||
// NewIPASettings generates the SRS, Q and precomputed weights for the barycentric formula.
|
||||
// The SRS is generated as common.VectorLength random points where the relative discrete log is
|
||||
// not known between each generator.
|
||||
func NewIPASettings() (*IPAConfig, error) {
|
||||
srs := GenerateRandomPoints(common.VectorLength)
|
||||
precompMSM, err := banderwagon.NewPrecompMSM(srs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating precomputed MSM: %s", err)
|
||||
}
|
||||
return &IPAConfig{
|
||||
SRS: srs,
|
||||
Q: banderwagon.Generator,
|
||||
PrecompMSM: precompMSM,
|
||||
PrecomputedWeights: NewPrecomputedWeights(),
|
||||
numRounds: computeNumRounds(common.VectorLength),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MultiScalar computes the multi scalar multiplication of points and scalars.
|
||||
// This is the variable-time path; it MUST only be called with public scalars
|
||||
// (verifier challenges, public commitments). Prover-side callers operating on
|
||||
// secret witness data MUST use MultiScalarBlinded instead.
|
||||
func MultiScalar(points []banderwagon.Element, scalars []fr.Element) (banderwagon.Element, error) {
|
||||
var result banderwagon.Element
|
||||
result.SetIdentity()
|
||||
|
||||
res, err := result.MultiExp(points, scalars, banderwagon.MultiExpConfig{NbTasks: runtime.NumCPU(), ScalarsMont: true})
|
||||
if err != nil {
|
||||
return banderwagon.Element{}, fmt.Errorf("mult exponentiation was not successful: %w", err)
|
||||
}
|
||||
|
||||
return *res, nil
|
||||
}
|
||||
|
||||
// MultiScalarBlinded computes the multi scalar multiplication of points and
|
||||
// scalars while masking the scalars from cache-timing side channels. Use this
|
||||
// for any prover-side call where the scalars are secret (witness halves,
|
||||
// blinding factors, polynomial openings). See banderwagon.MultiExpBlinded for
|
||||
// the construction.
|
||||
func MultiScalarBlinded(points []banderwagon.Element, scalars []fr.Element) (banderwagon.Element, error) {
|
||||
var result banderwagon.Element
|
||||
result.SetIdentity()
|
||||
|
||||
res, err := result.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{NbTasks: runtime.NumCPU(), ScalarsMont: true})
|
||||
if err != nil {
|
||||
return banderwagon.Element{}, fmt.Errorf("blinded mult exponentiation was not successful: %w", err)
|
||||
}
|
||||
|
||||
return *res, nil
|
||||
}
|
||||
|
||||
// Commit calculates the Pedersen Commitment of a polynomial in evaluation
|
||||
// form using the SRS. The polynomial coefficients are part of the prover's
|
||||
// witness, so we route through the blinded MSM path. This sacrifices the
|
||||
// PrecompMSM precomputed-table speedup in exchange for protection against
|
||||
// cache-timing recovery of witness scalars.
|
||||
//
|
||||
// The polynomial may be shorter than the SRS, in which case the trailing
|
||||
// coefficients are implicitly zero and we commit against the leading
|
||||
// len(polynomial) SRS generators. Callers in verkle/tree.go rely on this for
|
||||
// the [4]Fr "Cn-subtract" commitment in LeafNode.Delete (subtreeindex ∈
|
||||
// {2,3}); without short-poly support those sites trip the underlying
|
||||
// banderwagon length check.
|
||||
func (ic *IPAConfig) Commit(polynomial []fr.Element) banderwagon.Element {
|
||||
if len(polynomial) > len(ic.SRS) {
|
||||
panic(fmt.Sprintf("commit: polynomial length %d exceeds SRS length %d", len(polynomial), len(ic.SRS)))
|
||||
}
|
||||
res, err := MultiScalarBlinded(ic.SRS[:len(polynomial)], polynomial)
|
||||
if err != nil {
|
||||
// SRS length and polynomial length now match by construction; the only
|
||||
// remaining failure mode is crypto/rand exhaustion which is fatal.
|
||||
panic(fmt.Sprintf("commit: blinded MSM failed: %v", err))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// commit commits to a polynomial using the input group elements with the
|
||||
// variable-time MSM path. ONLY use this when scalars are public (verifier
|
||||
// side). Prover-side callers MUST use commitBlinded.
|
||||
func commit(groupElements []banderwagon.Element, polynomial []fr.Element) (banderwagon.Element, error) {
|
||||
if len(groupElements) != len(polynomial) {
|
||||
return banderwagon.Element{}, fmt.Errorf("group elements and polynomial are different sizes, %d != %d", len(groupElements), len(polynomial))
|
||||
}
|
||||
return MultiScalar(groupElements, polynomial)
|
||||
}
|
||||
|
||||
// commitBlinded commits to a polynomial using the input group elements via
|
||||
// the blinded (cache-timing-masked) MSM path. Use this for every prover-side
|
||||
// call site that operates on secret scalars.
|
||||
func commitBlinded(groupElements []banderwagon.Element, polynomial []fr.Element) (banderwagon.Element, error) {
|
||||
if len(groupElements) != len(polynomial) {
|
||||
return banderwagon.Element{}, fmt.Errorf("group elements and polynomial are different sizes, %d != %d", len(groupElements), len(polynomial))
|
||||
}
|
||||
return MultiScalarBlinded(groupElements, polynomial)
|
||||
}
|
||||
|
||||
// InnerProd computes the inner product of a and b.
|
||||
func InnerProd(a []fr.Element, b []fr.Element) (fr.Element, error) {
|
||||
if len(a) != len(b) {
|
||||
return fr.Element{}, fmt.Errorf("a and b are different sizes, %d != %d", len(a), len(b))
|
||||
}
|
||||
|
||||
result := fr.Zero()
|
||||
for i := 0; i < len(a); i++ {
|
||||
var tmp fr.Element
|
||||
|
||||
tmp.Mul(&a[i], &b[i])
|
||||
result.Add(&result, &tmp)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Computes c[i] =a[i] + b[i] * x
|
||||
// returns c
|
||||
func foldScalars(a []fr.Element, b []fr.Element, x fr.Element) ([]fr.Element, error) {
|
||||
if len(a) != len(b) {
|
||||
return nil, fmt.Errorf("slices not equal length")
|
||||
}
|
||||
result := make([]fr.Element, len(a))
|
||||
for i := 0; i < len(a); i++ {
|
||||
var bx fr.Element
|
||||
bx.Mul(&x, &b[i])
|
||||
result[i].Add(&bx, &a[i])
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Computes c[i] =a[i] + b[i] * x
|
||||
// returns c
|
||||
func foldPoints(a []banderwagon.Element, b []banderwagon.Element, x fr.Element) ([]banderwagon.Element, error) {
|
||||
if len(a) != len(b) {
|
||||
return nil, fmt.Errorf("slices not equal length")
|
||||
}
|
||||
|
||||
result := make([]banderwagon.Element, len(a))
|
||||
for i := 0; i < len(a); i++ {
|
||||
var bx banderwagon.Element
|
||||
bx.ScalarMul(&b[i], &x)
|
||||
result[i].Add(&bx, &a[i])
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Splits a slice of scalars into two slices of equal length
|
||||
// Eg [S1,S2,S3,S4] becomes [S1,S2] , [S3,S4]
|
||||
func splitScalars(x []fr.Element) ([]fr.Element, []fr.Element, error) {
|
||||
if len(x)%2 != 0 {
|
||||
return nil, nil, fmt.Errorf("slice should have an even length")
|
||||
}
|
||||
|
||||
mid := len(x) / 2
|
||||
return x[:mid], x[mid:], nil
|
||||
}
|
||||
|
||||
// Splits a slice of points into two slices of equal length
|
||||
// Eg [P1,P2,P3,P4,P5,P6] becomes [P1,P2,P3] , [P4,P5,P6]
|
||||
func splitPoints(x []banderwagon.Element) ([]banderwagon.Element, []banderwagon.Element, error) {
|
||||
if len(x)%2 != 0 {
|
||||
return nil, nil, fmt.Errorf("slice should have an even length")
|
||||
}
|
||||
mid := len(x) / 2
|
||||
|
||||
return x[:mid], x[mid:], nil
|
||||
}
|
||||
|
||||
// This function does log2(vector_size)
|
||||
//
|
||||
// Since we do not allow for 0 size vectors, this is checked
|
||||
// since we also do not allow for vectors which are not powers of 2, this is also checked
|
||||
//
|
||||
// It is okay to panic here, because the input is a constant, so it will panic before
|
||||
// any proofs are made.
|
||||
func computeNumRounds(vectorSize uint32) uint32 {
|
||||
// Check if this number is 0
|
||||
// zero is not a valid input to this function for our usecase
|
||||
if vectorSize == 0 {
|
||||
panic("zero is not a valid input")
|
||||
}
|
||||
|
||||
// See: https://stackoverflow.com/a/600306
|
||||
isPow2 := (vectorSize & (vectorSize - 1)) == 0
|
||||
|
||||
if !isPow2 {
|
||||
panic("non power of 2 numbers are not valid inputs")
|
||||
}
|
||||
|
||||
res := math.Log2(float64(vectorSize))
|
||||
|
||||
return uint32(res)
|
||||
}
|
||||
|
||||
// GenerateRandomPoints generates numPoints random points on the curve using
|
||||
// hardcoded seed.
|
||||
func GenerateRandomPoints(numPoints uint64) []banderwagon.Element {
|
||||
seed := "eth_verkle_oct_2021"
|
||||
|
||||
points := []banderwagon.Element{}
|
||||
|
||||
var increment uint64 = 0
|
||||
|
||||
for uint64(len(points)) != numPoints {
|
||||
|
||||
digest := sha256.New()
|
||||
digest.Write([]byte(seed))
|
||||
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, increment)
|
||||
digest.Write(b)
|
||||
|
||||
hash := digest.Sum(nil)
|
||||
|
||||
var x fp.Element
|
||||
x.SetBytes(hash)
|
||||
|
||||
increment++
|
||||
|
||||
x_as_bytes := x.Bytes()
|
||||
var point_found banderwagon.Element
|
||||
err := point_found.SetBytes(x_as_bytes[:])
|
||||
if err != nil {
|
||||
// This point is not in the correct subgroup or on the curve
|
||||
continue
|
||||
}
|
||||
points = append(points, point_found)
|
||||
|
||||
}
|
||||
|
||||
return points
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/test_helper"
|
||||
)
|
||||
|
||||
var ipaConf *IPAConfig
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
ipaConf, err = NewIPASettings()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestIPAProofCreateVerify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Shared View
|
||||
var point fr.Element
|
||||
point.SetUint64(123456789)
|
||||
|
||||
// Prover view
|
||||
poly := test_helper.TestPoly256(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
|
||||
prover_comm := ipaConf.Commit(poly)
|
||||
|
||||
prover_transcript := common.NewTranscript("ipa")
|
||||
|
||||
proof, err := CreateIPAProof(prover_transcript, ipaConf, prover_comm, poly, point)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create proof: %s", err)
|
||||
}
|
||||
|
||||
lagrange_coeffs := ipaConf.PrecomputedWeights.ComputeBarycentricCoefficients(point)
|
||||
inner_product, err := InnerProd(poly, lagrange_coeffs)
|
||||
if err != nil {
|
||||
t.Fatalf("could not compute inner product: %s", err)
|
||||
}
|
||||
|
||||
test_serialize_deserialize_proof(t, proof)
|
||||
|
||||
// Verifier view
|
||||
verifier_comm := prover_comm // In reality, the verifier will rebuild this themselves
|
||||
verifier_transcript := common.NewTranscript("ipa")
|
||||
|
||||
ok, err := CheckIPAProof(verifier_transcript, ipaConf, verifier_comm, proof, point, inner_product)
|
||||
if err != nil {
|
||||
t.Fatalf("could not check proof: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("inner product proof failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPAConsistencySimpleProof(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Shared View
|
||||
var input_point fr.Element
|
||||
input_point.SetUint64(2101)
|
||||
|
||||
// Prover view
|
||||
//
|
||||
poly := test_helper.TestPoly256(
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
)
|
||||
|
||||
prover_comm := ipaConf.Commit(poly)
|
||||
test_helper.PointEqualHex(t, prover_comm, "1b9dff8f5ebbac250d291dfe90e36283a227c64b113c37f1bfb9e7a743cdb128")
|
||||
|
||||
prover_transcript := common.NewTranscript("test")
|
||||
proof, err := CreateIPAProof(prover_transcript, ipaConf, prover_comm, poly, input_point)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create proof: %s", err)
|
||||
}
|
||||
|
||||
lagrange_coeffs := ipaConf.PrecomputedWeights.ComputeBarycentricCoefficients(input_point)
|
||||
output_point, err := InnerProd(poly, lagrange_coeffs)
|
||||
if err != nil {
|
||||
t.Fatalf("could not compute inner product: %s", err)
|
||||
}
|
||||
test_helper.ScalarEqualHex(t, output_point, "4a353e70b03c89f161de002e8713beec0d740a5e20722fd5bd68b30540a33208")
|
||||
|
||||
// Lets check the state of the transcript, by squeezing out a challenge
|
||||
p_challenge := prover_transcript.ChallengeScalar([]byte("state"))
|
||||
test_helper.ScalarEqualHex(t, p_challenge, "0a81881cbfd7d7197a54ebd67ed6a68b5867f3c783706675b34ece43e85e7306")
|
||||
|
||||
// Note, that we can be confident that any implementation which passes the above conditions
|
||||
// will have a proof object that is consistent, as the transcript adds everything into the proof
|
||||
|
||||
// Verifier view
|
||||
//
|
||||
verifier_comm := prover_comm // In reality, the verifier will rebuild this themselves
|
||||
verifier_transcript := common.NewTranscript("test")
|
||||
|
||||
ok, err := CheckIPAProof(verifier_transcript, ipaConf, verifier_comm, proof, input_point, output_point)
|
||||
if err != nil {
|
||||
t.Fatalf("could not check proof: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("inner product proof failed")
|
||||
}
|
||||
//
|
||||
v_challenge := verifier_transcript.ChallengeScalar([]byte("state"))
|
||||
if !v_challenge.Equal(&p_challenge) {
|
||||
t.Fatal("prover and verifier state are not the same. The proof should not have passed!")
|
||||
}
|
||||
|
||||
// Check that the serialised proof matches the other implementations
|
||||
expected := "273395a8febdaed38e94c3d874e99c911a47dd84616d54c55021d5c4131b507e46a4ec2c7e82b77ec2f533994c91ca7edaef212c666a1169b29c323eabb0cf690e0146638d0e2d543f81da4bd597bf3013e1663f340a8f87b845495598d0a3951590b6417f868edaeb3424ff174901d1185a53a3ee127fb7be0af42dda44bf992885bde279ef821a298087717ef3f2b78b2ede7f5d2ea1b60a4195de86a530eb247fd7e456012ae9a070c61635e55d1b7a340dfab8dae991d6273d099d9552815434cc1ba7bcdae341cf7928c6f25102370bdf4b26aad3af654d9dff4b3735661db3177342de5aad774a59d3e1b12754aee641d5f9cd1ecd2751471b308d2d8410add1c9fcc5a2b7371259f0538270832a98d18151f653efbc60895fab8be9650510449081626b5cd24671d1a3253487d44f589c2ff0da3557e307e520cf4e0054bbf8bdffaa24b7e4cce5092ccae5a08281ee24758374f4e65f126cacce64051905b5e2038060ad399c69ca6cb1d596d7c9cb5e161c7dcddc1a7ad62660dd4a5f69b31229b80e6b3df520714e4ea2b5896ebd48d14c7455e91c1ecf4acc5ffb36937c49413b7d1005dd6efbd526f5af5d61131ca3fcdae1218ce81c75e62b39100ec7f474b48a2bee6cef453fa1bc3db95c7c6575bc2d5927cbf7413181ac905766a4038a7b422a8ef2bf7b5059b5c546c19a33c1049482b9a9093f864913ca82290decf6e9a65bf3f66bc3ba4a8ed17b56d890a83bcbe74435a42499dec115"
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := proof.Write(buf); err != nil {
|
||||
t.Fatal("could not serialise proof")
|
||||
}
|
||||
|
||||
bytes := buf.Bytes()
|
||||
if expected != hex.EncodeToString(bytes) {
|
||||
t.Fatal("expected serialised proof is different from the other implementations")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicInnerProduct(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var a []fr.Element
|
||||
for i := 0; i < 10; i++ {
|
||||
var tmp fr.Element
|
||||
tmp.SetUint64(uint64(i))
|
||||
a = append(a, tmp)
|
||||
}
|
||||
var b []fr.Element
|
||||
for i := 0; i < 10; i++ {
|
||||
var tmp fr.Element
|
||||
tmp.SetOne()
|
||||
b = append(b, tmp)
|
||||
}
|
||||
|
||||
got, err := InnerProd(a, b)
|
||||
if err != nil {
|
||||
t.Fatalf("could not compute inner product: %s", err)
|
||||
}
|
||||
expected := fr.Zero()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
var tmp fr.Element
|
||||
tmp.SetUint64(uint64(i))
|
||||
expected.Add(&expected, &tmp)
|
||||
}
|
||||
if !got.Equal(&expected) {
|
||||
t.Fatal("the inner product should just be the sum of a since b is just 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasicCommit(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gen := banderwagon.Generator
|
||||
|
||||
var generators []banderwagon.Element
|
||||
for i := 0; i < 5; i++ {
|
||||
generators = append(generators, gen)
|
||||
}
|
||||
|
||||
var a []fr.Element
|
||||
for i := 0; i < 5; i++ {
|
||||
var tmp fr.Element
|
||||
_, err := tmp.SetRandom()
|
||||
if err != nil {
|
||||
t.Fatal("could not generate randomness")
|
||||
}
|
||||
a = append(a, tmp)
|
||||
}
|
||||
got, err := commit(generators, a)
|
||||
if err != nil {
|
||||
t.Fatalf("could not compute inner product: %s", err)
|
||||
}
|
||||
|
||||
total := fr.Zero()
|
||||
for i := 0; i < 5; i++ {
|
||||
total.Add(&total, &a[i])
|
||||
}
|
||||
|
||||
var expected banderwagon.Element
|
||||
expected.ScalarMul(&gen, &total)
|
||||
|
||||
if !got.Equal(&expected) {
|
||||
t.Fatal("commit function; incorrect results")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCRSGeneration(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
generator := banderwagon.Generator
|
||||
points := GenerateRandomPoints(256)
|
||||
for _, point := range points {
|
||||
if !point.IsOnCurve() {
|
||||
t.Fatal("generated a point that was not on the curve")
|
||||
}
|
||||
// Check point is in the correct subgroup by doing
|
||||
// serialise deserialise roundtrip
|
||||
|
||||
bytes := point.Bytes()
|
||||
err := point.SetBytes(bytes[:])
|
||||
if err != nil {
|
||||
t.Fatal("point is not in the banderwagon subgroup")
|
||||
}
|
||||
if point.Equal(&generator) {
|
||||
t.Fatal("one of the generated points was the generator. The inner product point is being used as the generator.")
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the points are all unique
|
||||
points = removeDuplicatePoints(points)
|
||||
if len(points) != 256 {
|
||||
t.Fatal("points contained duplicates")
|
||||
}
|
||||
|
||||
// Check against Verkle trie test vectors (see hackmd.io/1RcGSMQgT4uREaq1CCx_cg).
|
||||
bytes := points[0].Bytes()
|
||||
got := hex.EncodeToString(bytes[:])
|
||||
expected := "01587ad1336675eb912550ec2a28eb8923b824b490dd2ba82e48f14590a298a0"
|
||||
if got != expected {
|
||||
t.Fatal("the first point is not correct")
|
||||
}
|
||||
bytes = points[255].Bytes()
|
||||
got = hex.EncodeToString(bytes[:])
|
||||
expected = "3de2be346b539395b0c0de56a5ccca54a317f1b5c80107b0802af9a62276a4d8"
|
||||
if got != expected {
|
||||
t.Fatal("the 256th (last) point is not correct")
|
||||
}
|
||||
|
||||
digest := sha256.New()
|
||||
for _, point := range points {
|
||||
bytes := point.Bytes()
|
||||
digest.Write(bytes[:])
|
||||
}
|
||||
hash := digest.Sum(nil)
|
||||
got = hex.EncodeToString(hash[:])
|
||||
expected = "1fcaea10bf24f750200e06fa473c76ff0468007291fa548e2d99f09ba9256fdb"
|
||||
if got != expected {
|
||||
t.Fatal("unexpected point encountered")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInsideDomainEvaluation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Define some arbitrary polynomial in evaluation form.
|
||||
poly := test_helper.TestPoly256(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
|
||||
polyComm := ipaConf.Commit(poly)
|
||||
|
||||
// For all the possible evaluation points *in the domain*, double check that
|
||||
// proof generation and verification works correctly.
|
||||
for domainEvalPoint := 0; domainEvalPoint < domainSize; domainEvalPoint++ {
|
||||
var frEvalPoint fr.Element
|
||||
frEvalPoint.SetUint64(uint64(domainEvalPoint))
|
||||
|
||||
// Prover.
|
||||
transcript := common.NewTranscript("ipa")
|
||||
proof, err := CreateIPAProof(transcript, ipaConf, polyComm, poly, frEvalPoint)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create proof: %s", err)
|
||||
}
|
||||
|
||||
// Verifier.
|
||||
transcript = common.NewTranscript("ipa")
|
||||
// We check the proof against what we *know* to be correct regarding `poly` definition in evaluation form.
|
||||
ok, err := CheckIPAProof(transcript, ipaConf, polyComm, proof, frEvalPoint, poly[domainEvalPoint])
|
||||
if err != nil {
|
||||
t.Fatalf("could not check proof: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatal("inner product proof failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func test_serialize_deserialize_proof(t *testing.T, proof IPAProof) {
|
||||
buf := new(bytes.Buffer)
|
||||
if err := proof.Write(buf); err != nil {
|
||||
t.Fatal("failed to write proof")
|
||||
}
|
||||
|
||||
var got_proof IPAProof
|
||||
if err := got_proof.Read(buf); err != nil {
|
||||
t.Fatal("failed to read proof")
|
||||
}
|
||||
|
||||
if !got_proof.Equal(proof) {
|
||||
t.Fatal("proof serialization does not match deserialization for IPA")
|
||||
}
|
||||
}
|
||||
|
||||
func removeDuplicatePoints(intSlice []banderwagon.Element) []banderwagon.Element {
|
||||
allKeys := make(map[banderwagon.Element]bool)
|
||||
list := []banderwagon.Element{}
|
||||
for _, item := range intSlice {
|
||||
if _, value := allKeys[item]; !value {
|
||||
allKeys[item] = true
|
||||
list = append(list, item)
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
)
|
||||
|
||||
var maxEvalPointInsideDomain fr.Element
|
||||
|
||||
func init() {
|
||||
maxEvalPointInsideDomain.SetUint64(common.VectorLength - 1)
|
||||
}
|
||||
|
||||
// The following are unexported labels to be used in Fiat-Shamir during the
|
||||
// inner-product argument protocol.
|
||||
//
|
||||
// The following is a short description on how they're used in the protocol:
|
||||
// 1. Append the domain separator. (labelDomainSep)
|
||||
// 2. Append the commitment to the polynomial. (labelC)
|
||||
// 3. Append the input point. (labelInputPoint)
|
||||
// 4. Append the output point. (labelOutputPoint)
|
||||
// 5. Pull the re-scaling factor `w` to scale Q. (labelW).
|
||||
// 6. For each round of the IPA protocol:
|
||||
// a. Append the resulting point C_L. (labelL)
|
||||
// b. Append the resulting point C_R. (labelR)
|
||||
// c. Pull the random scalar-field element `x`. (labelX)
|
||||
//
|
||||
// Note: this package must not mutate these label values, nor pass them to
|
||||
// parts of the code that would mutate them.
|
||||
|
||||
var (
|
||||
labelDomainSep = []byte("ipa")
|
||||
labelC = []byte("C")
|
||||
labelInputPoint = []byte("input point")
|
||||
labelOutputPoint = []byte("output point")
|
||||
labelW = []byte("w")
|
||||
labelL = []byte("L")
|
||||
labelR = []byte("R")
|
||||
labelX = []byte("x")
|
||||
)
|
||||
|
||||
// IPAProof is an inner product argument proof.
|
||||
type IPAProof struct {
|
||||
L []banderwagon.Element
|
||||
R []banderwagon.Element
|
||||
A_scalar fr.Element
|
||||
}
|
||||
|
||||
// CreateIPAProof creates an IPA proof for a committed polynomial in evaluation form.
|
||||
// `a` are the evaluation of the polynomial in the domain, and `evalPoint` represents the
|
||||
// evaluation point. The evaluation of the polynomial at such point is computed automatically.
|
||||
func CreateIPAProof(transcript *common.Transcript, ic *IPAConfig, commitment banderwagon.Element, a []fr.Element, evalPoint fr.Element) (IPAProof, error) {
|
||||
transcript.DomainSep(labelDomainSep)
|
||||
|
||||
b := computeBVector(ic, evalPoint)
|
||||
|
||||
inner_prod, err := InnerProd(a, b)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not compute inner product: %w", err)
|
||||
}
|
||||
|
||||
transcript.AppendPoint(&commitment, labelC)
|
||||
transcript.AppendScalar(&evalPoint, labelInputPoint)
|
||||
transcript.AppendScalar(&inner_prod, labelOutputPoint)
|
||||
w := transcript.ChallengeScalar(labelW)
|
||||
|
||||
var q banderwagon.Element
|
||||
q.ScalarMul(&ic.Q, &w)
|
||||
|
||||
num_rounds := ic.numRounds
|
||||
|
||||
current_basis := ic.SRS
|
||||
|
||||
L := make([]banderwagon.Element, num_rounds)
|
||||
R := make([]banderwagon.Element, num_rounds)
|
||||
|
||||
for i := 0; i < int(num_rounds); i++ {
|
||||
|
||||
a_L, a_R, err := splitScalars(a)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not split a scalars: %w", err)
|
||||
}
|
||||
|
||||
b_L, b_R, err := splitScalars(b)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not split b scalars: %w", err)
|
||||
}
|
||||
|
||||
G_L, G_R, err := splitPoints(current_basis)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not split G points: %w", err)
|
||||
}
|
||||
|
||||
z_L, err := InnerProd(a_R, b_L)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not compute a_r*b_L inner product: %w", err)
|
||||
}
|
||||
z_R, err := InnerProd(a_L, b_R)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not compute a_L*b_R inner product: %w", err)
|
||||
}
|
||||
|
||||
// All four MSMs below operate on secret prover-side scalars
|
||||
// (witness halves a_L/a_R and the blinding-style scalars z_L/z_R),
|
||||
// so we route through commitBlinded which masks the scalars from
|
||||
// cache-timing side channels. See banderwagon.MultiExpBlinded.
|
||||
C_L_1, err := commitBlinded(G_L, a_R)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not do G_L*a_R MSM: %w", err)
|
||||
}
|
||||
C_L, err := commitBlinded([]banderwagon.Element{C_L_1, q}, []fr.Element{fr.One(), z_L})
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not do C_L_1+z_L*q MSM: %w", err)
|
||||
}
|
||||
|
||||
C_R_1, err := commitBlinded(G_R, a_L)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not do G_R*a_L MSM: %w", err)
|
||||
}
|
||||
C_R, err := commitBlinded([]banderwagon.Element{C_R_1, q}, []fr.Element{fr.One(), z_R})
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not do C_R_1+z_R*q MSM: %w", err)
|
||||
}
|
||||
|
||||
L[i] = C_L
|
||||
R[i] = C_R
|
||||
|
||||
transcript.AppendPoint(&C_L, labelL)
|
||||
transcript.AppendPoint(&C_R, labelR)
|
||||
x := transcript.ChallengeScalar(labelX)
|
||||
|
||||
var xInv fr.Element
|
||||
xInv.Inverse(&x)
|
||||
|
||||
// Fold scalars and points for this round of the IPA reduction.
|
||||
a, err = foldScalars(a_L, a_R, x)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not fold a scalars a_L and a_R with x: %w", err)
|
||||
}
|
||||
b, err = foldScalars(b_L, b_R, xInv)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not fold b scalars b_L and b_R with xInv: %w", err)
|
||||
}
|
||||
|
||||
current_basis, err = foldPoints(G_L, G_R, xInv)
|
||||
if err != nil {
|
||||
return IPAProof{}, fmt.Errorf("could not fold points G_L and G_R with xInv: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if len(a) != 1 {
|
||||
return IPAProof{}, fmt.Errorf("length of `a` should be 1 at the end of the reduction")
|
||||
}
|
||||
|
||||
return IPAProof{
|
||||
L: L,
|
||||
R: R,
|
||||
A_scalar: a[0],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Write serializes the IPA proof to the given writer.
|
||||
func (ip *IPAProof) Write(w io.Writer) error {
|
||||
for _, el := range ip.L {
|
||||
if err := binary.Write(w, binary.BigEndian, el.Bytes()); err != nil {
|
||||
return fmt.Errorf("failed to write L: %w", err)
|
||||
}
|
||||
}
|
||||
for _, ar := range ip.R {
|
||||
if err := binary.Write(w, binary.BigEndian, ar.Bytes()); err != nil {
|
||||
return fmt.Errorf("failed to write R: %w", err)
|
||||
}
|
||||
}
|
||||
if err := binary.Write(w, binary.BigEndian, ip.A_scalar.BytesLE()); err != nil {
|
||||
return fmt.Errorf("failed to write A_scalar: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read deserializes the IPA proof from the given reader.
|
||||
func (ip *IPAProof) Read(r io.Reader) error {
|
||||
var L []banderwagon.Element
|
||||
for i := 0; i < 8; i++ {
|
||||
L_i, err := common.ReadPoint(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read L[%d]: %w", i, err)
|
||||
}
|
||||
L = append(L, *L_i)
|
||||
}
|
||||
ip.L = L
|
||||
var R []banderwagon.Element
|
||||
for i := 0; i < 8; i++ {
|
||||
R_i, err := common.ReadPoint(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read R[%d]: %w", i, err)
|
||||
}
|
||||
R = append(R, *R_i)
|
||||
}
|
||||
ip.R = R
|
||||
|
||||
A_Scalar, err := common.ReadScalar(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read A_scalar: %w", err)
|
||||
}
|
||||
ip.A_scalar = *A_Scalar
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal checks if two IPA proofs are equal.
|
||||
func (ip IPAProof) Equal(other IPAProof) bool {
|
||||
num_rounds := 8
|
||||
if len(ip.L) != len(other.L) {
|
||||
return false
|
||||
}
|
||||
if len(ip.R) != len(other.R) {
|
||||
return false
|
||||
}
|
||||
if len(ip.L) != len(ip.R) {
|
||||
return false
|
||||
}
|
||||
if len(ip.L) != num_rounds {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < num_rounds; i++ {
|
||||
expect_L_i := ip.L[i]
|
||||
expect_R_i := ip.R[i]
|
||||
|
||||
got_L_i := other.L[i]
|
||||
got_R_i := other.R[i]
|
||||
|
||||
if !expect_L_i.Equal(&got_L_i) {
|
||||
return false
|
||||
}
|
||||
if !expect_R_i.Equal(&got_R_i) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return ip.A_scalar.Equal(&other.A_scalar)
|
||||
}
|
||||
|
||||
func computeBVector(ic *IPAConfig, evalPoint fr.Element) []fr.Element {
|
||||
if evalPoint.Cmp(&maxEvalPointInsideDomain) > 0 {
|
||||
return ic.PrecomputedWeights.ComputeBarycentricCoefficients(evalPoint)
|
||||
}
|
||||
// We build b = [0, 0, 0, ... , 1, .., 0] where the 1 element is at the index of the evaluation point.
|
||||
// This is correct since innerProductArgument(a, b) will return the evaluation of the polynomial at the
|
||||
// evaluation point in the domain.
|
||||
b := make([]fr.Element, common.VectorLength)
|
||||
var evalPointBI big.Int
|
||||
evalPoint.ToBigIntRegular(&evalPointBI)
|
||||
// Uint64() is safe because we checked that evalPoint is inside the domain (i.e <256).
|
||||
b[evalPointBI.Uint64()] = fr.One()
|
||||
|
||||
return b
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
// Copyright (C) 2026 Lux Industries Inc. All rights reserved.
|
||||
// Licensed under Apache-2.0 OR MIT.
|
||||
//
|
||||
// Tests for the scalar-blinding wrapper around banderwagon.MultiExp.
|
||||
// Two properties are checked:
|
||||
//
|
||||
// 1. Correctness: MultiExpBlinded(points, scalars) must produce the same
|
||||
// group element as MultiExp(points, scalars) on identical inputs. This
|
||||
// is a hard equality requirement — the blinding must cancel exactly.
|
||||
// 2. Timing variance: across many invocations on the same scalar input,
|
||||
// the blinded path should exhibit non-trivial wall-clock variance
|
||||
// because a fresh r is drawn per call. This is an indication, not a
|
||||
// proof, that the trace observed by an attacker depends on r rather
|
||||
// than on the (constant) underlying scalars.
|
||||
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
)
|
||||
|
||||
// randomScalars returns n random Fr elements in Montgomery form (matching
|
||||
// the convention used by every prover-side caller and the ScalarsMont: true
|
||||
// flag passed to MultiExp).
|
||||
func randomScalars(t *testing.T, n int) []fr.Element {
|
||||
t.Helper()
|
||||
s := make([]fr.Element, n)
|
||||
for i := range s {
|
||||
if _, err := s[i].SetRandom(); err != nil {
|
||||
t.Fatalf("SetRandom: %v", err)
|
||||
}
|
||||
// SetRandom returns a regular-form element; convert to Montgomery
|
||||
// form so subsequent arithmetic and MultiExp(ScalarsMont: true)
|
||||
// see consistent representation.
|
||||
s[i].ToMont()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// TestMultiExpBlinded_Correctness verifies that the blinded MSM produces
|
||||
// the same group element as the unblinded MSM across 100 random
|
||||
// (points, scalars) pairs at multiple input lengths. This is the
|
||||
// load-bearing property: the blinding must cancel exactly so that proofs
|
||||
// produced through the blinded path verify under the unblinded verifier.
|
||||
func TestMultiExpBlinded_Correctness(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Use the SRS as a convenient source of group elements. The SRS is
|
||||
// public and 256 elements long, matching common.VectorLength.
|
||||
if ipaConf == nil {
|
||||
t.Fatal("ipaConf not initialized; TestMain should have set it")
|
||||
}
|
||||
srs := ipaConf.SRS
|
||||
|
||||
// Sweep a few input sizes, including the 2-element shape used by the
|
||||
// four prover-side commitBlinded calls and the full vector length used
|
||||
// by IPAConfig.Commit.
|
||||
sizes := []int{2, 4, 8, 64, 128, int(common.VectorLength)}
|
||||
|
||||
totalChecks := 0
|
||||
for _, sz := range sizes {
|
||||
points := srs[:sz]
|
||||
// 100 random scalar vectors per size.
|
||||
for trial := 0; trial < 100; trial++ {
|
||||
scalars := randomScalars(t, sz)
|
||||
|
||||
var unblinded banderwagon.Element
|
||||
unblinded.SetIdentity()
|
||||
if _, err := unblinded.MultiExp(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true}); err != nil {
|
||||
t.Fatalf("size=%d trial=%d MultiExp: %v", sz, trial, err)
|
||||
}
|
||||
|
||||
var blinded banderwagon.Element
|
||||
blinded.SetIdentity()
|
||||
if _, err := blinded.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true}); err != nil {
|
||||
t.Fatalf("size=%d trial=%d MultiExpBlinded: %v", sz, trial, err)
|
||||
}
|
||||
|
||||
if !blinded.Equal(&unblinded) {
|
||||
t.Fatalf("size=%d trial=%d: blinded result != unblinded result", sz, trial)
|
||||
}
|
||||
totalChecks++
|
||||
}
|
||||
}
|
||||
t.Logf("verified %d blinded-vs-unblinded equality checks across %d input sizes", totalChecks, len(sizes))
|
||||
}
|
||||
|
||||
// TestMultiExpBlinded_DoesNotMutateInputs ensures the helper never modifies
|
||||
// the caller's scalar slice. The IPA prover passes witness halves directly,
|
||||
// so silent mutation would corrupt the proof transcript.
|
||||
func TestMultiExpBlinded_DoesNotMutateInputs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const n = 64
|
||||
points := ipaConf.SRS[:n]
|
||||
scalars := randomScalars(t, n)
|
||||
|
||||
// Snapshot.
|
||||
snapshot := make([]fr.Element, n)
|
||||
copy(snapshot, scalars)
|
||||
|
||||
var p banderwagon.Element
|
||||
p.SetIdentity()
|
||||
if _, err := p.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true}); err != nil {
|
||||
t.Fatalf("MultiExpBlinded: %v", err)
|
||||
}
|
||||
|
||||
for i := range scalars {
|
||||
if !scalars[i].Equal(&snapshot[i]) {
|
||||
t.Fatalf("scalar[%d] mutated by MultiExpBlinded", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiExpBlinded_LengthMismatch checks the explicit length-equality
|
||||
// guard so that a wiring bug at a call site fails loudly instead of being
|
||||
// papered over by the underlying MSM panicking.
|
||||
func TestMultiExpBlinded_LengthMismatch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
points := ipaConf.SRS[:4]
|
||||
scalars := randomScalars(t, 5)
|
||||
|
||||
var p banderwagon.Element
|
||||
p.SetIdentity()
|
||||
_, err := p.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true})
|
||||
if err == nil {
|
||||
t.Fatal("expected length-mismatch error, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestMultiExpBlinded_TimingVariance is an indication-not-proof that
|
||||
// MultiExpBlinded introduces per-call variance. We time 1000 iterations on a
|
||||
// fixed scalar input and assert the standard deviation exceeds a small
|
||||
// threshold (0.1% of the mean). Pippenger on identical inputs is highly
|
||||
// repeatable, so without blinding the variance would collapse to noise from
|
||||
// scheduling alone. A meaningful std-dev indicates the trace differs per
|
||||
// call due to the fresh r.
|
||||
//
|
||||
// This is not a side-channel proof. It is a smoke test that the masking
|
||||
// path is actually being exercised. Real evaluation must use a hardware
|
||||
// cache-timing rig.
|
||||
func TestMultiExpBlinded_TimingVariance(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping timing variance test in -short mode")
|
||||
}
|
||||
t.Parallel()
|
||||
|
||||
const n = 32
|
||||
const iterations = 1000
|
||||
|
||||
points := ipaConf.SRS[:n]
|
||||
scalars := randomScalars(t, n)
|
||||
|
||||
// Warm-up to amortize first-touch costs.
|
||||
for i := 0; i < 20; i++ {
|
||||
var p banderwagon.Element
|
||||
p.SetIdentity()
|
||||
if _, err := p.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true}); err != nil {
|
||||
t.Fatalf("warmup: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
samples := make([]float64, iterations)
|
||||
for i := 0; i < iterations; i++ {
|
||||
var p banderwagon.Element
|
||||
p.SetIdentity()
|
||||
start := time.Now()
|
||||
if _, err := p.MultiExpBlinded(points, scalars, banderwagon.MultiExpConfig{ScalarsMont: true}); err != nil {
|
||||
t.Fatalf("iter %d: %v", i, err)
|
||||
}
|
||||
samples[i] = float64(time.Since(start).Nanoseconds())
|
||||
}
|
||||
|
||||
mean := 0.0
|
||||
for _, s := range samples {
|
||||
mean += s
|
||||
}
|
||||
mean /= float64(iterations)
|
||||
|
||||
variance := 0.0
|
||||
for _, s := range samples {
|
||||
d := s - mean
|
||||
variance += d * d
|
||||
}
|
||||
variance /= float64(iterations)
|
||||
std := math.Sqrt(variance)
|
||||
|
||||
t.Logf("MultiExpBlinded over %d iters: mean=%.0fns std=%.0fns (cv=%.2f%%)", iterations, mean, std, 100*std/mean)
|
||||
|
||||
// We expect a non-trivial std-dev. If std is tiny relative to the mean
|
||||
// either the blinding is being optimized away or the timing rig is
|
||||
// unable to resolve it — either way, flag it. Threshold deliberately
|
||||
// loose; this is an indication, not a constant-time proof.
|
||||
if std < mean*0.001 {
|
||||
t.Fatalf("std-dev %.0fns is suspiciously small relative to mean %.0fns; blinding may not be exercised", std, mean)
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package ipa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
)
|
||||
|
||||
// CheckIPAProof verifies an IPA proof for a committed polynomial in evaluation form.
|
||||
// It verifies that `proof` is a valid proof for the polynomial at the evaluation
|
||||
// point `evalPoint` with result `result`
|
||||
func CheckIPAProof(transcript *common.Transcript, ic *IPAConfig, commitment banderwagon.Element, proof IPAProof, evalPoint fr.Element, result fr.Element) (bool, error) {
|
||||
transcript.DomainSep(labelDomainSep)
|
||||
|
||||
if len(proof.L) != len(proof.R) {
|
||||
return false, fmt.Errorf("vectors L and R should be the same size")
|
||||
}
|
||||
if len(proof.L) != int(ic.numRounds) {
|
||||
return false, fmt.Errorf("the number of points for L and R should be equal to the number of rounds")
|
||||
}
|
||||
|
||||
b := computeBVector(ic, evalPoint)
|
||||
|
||||
transcript.AppendPoint(&commitment, labelC)
|
||||
transcript.AppendScalar(&evalPoint, labelInputPoint)
|
||||
transcript.AppendScalar(&result, labelOutputPoint)
|
||||
|
||||
w := transcript.ChallengeScalar(labelW)
|
||||
|
||||
// Rescaling of q.
|
||||
var q banderwagon.Element
|
||||
q.ScalarMul(&ic.Q, &w)
|
||||
|
||||
var qy banderwagon.Element
|
||||
qy.ScalarMul(&q, &result)
|
||||
commitment.Add(&commitment, &qy)
|
||||
|
||||
challenges := generateChallenges(transcript, &proof)
|
||||
challengesInv := fr.BatchInvert(challenges)
|
||||
|
||||
// Compute expected commitment
|
||||
var err error
|
||||
for i := 0; i < len(challenges); i++ {
|
||||
x := challenges[i]
|
||||
L := proof.L[i]
|
||||
R := proof.R[i]
|
||||
|
||||
commitment, err = commit([]banderwagon.Element{commitment, L, R}, []fr.Element{fr.One(), x, challengesInv[i]})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not compute commitment+x*L+x^-1*R: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
g := ic.SRS
|
||||
|
||||
// We compute the folding-scalars for g and b.
|
||||
foldingScalars := make([]fr.Element, len(g))
|
||||
for i := 0; i < len(g); i++ {
|
||||
scalar := fr.One()
|
||||
|
||||
for challengeIdx := 0; challengeIdx < len(challenges); challengeIdx++ {
|
||||
if i&(1<<(7-challengeIdx)) > 0 {
|
||||
scalar.Mul(&scalar, &challengesInv[challengeIdx])
|
||||
}
|
||||
}
|
||||
foldingScalars[i] = scalar
|
||||
}
|
||||
g0, err := MultiScalar(g, foldingScalars)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not compute g0: %w", err)
|
||||
}
|
||||
b0, err := InnerProd(b, foldingScalars)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not compute b0: %w", err)
|
||||
}
|
||||
|
||||
var got banderwagon.Element
|
||||
// g0 * a + (a * b) * Q;
|
||||
var part_1 banderwagon.Element
|
||||
part_1.ScalarMul(&g0, &proof.A_scalar)
|
||||
|
||||
var part_2 banderwagon.Element
|
||||
var part_2a fr.Element
|
||||
|
||||
part_2a.Mul(&b0, &proof.A_scalar)
|
||||
part_2.ScalarMul(&q, &part_2a)
|
||||
|
||||
got.Add(&part_1, &part_2)
|
||||
|
||||
return got.Equal(&commitment), nil
|
||||
}
|
||||
|
||||
func generateChallenges(transcript *common.Transcript, proof *IPAProof) []fr.Element {
|
||||
|
||||
challenges := make([]fr.Element, len(proof.L))
|
||||
for i := 0; i < len(proof.L); i++ {
|
||||
transcript.AppendPoint(&proof.L[i], labelL)
|
||||
transcript.AppendPoint(&proof.R[i], labelR)
|
||||
challenges[i] = transcript.ChallengeScalar(labelX)
|
||||
}
|
||||
return challenges
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
package multiproof
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/ipa"
|
||||
)
|
||||
|
||||
// The following are unexported labels to be used in Fiat-Shamir during the
|
||||
// multiproof protocol.
|
||||
//
|
||||
// The following is a short description on how they're used in the protocol:
|
||||
// 1. Append the domain separator. (labelDomainSep)
|
||||
// 2. For each opening, we append to the transcript:
|
||||
// a. The polynomial commitment (labelC).
|
||||
// b. The evaluation point (labelZ).
|
||||
// c. The evaluation result (labelY).
|
||||
// 3. Pull a scalar-field element from the transcript to be used for
|
||||
// the random linear combination of openings. (labelR)
|
||||
// 4. Append point D which is sum(r^i * (f_i(x)-y_i)/(x-z_i)). (labelD)
|
||||
// 5. Pull a random scalar-field to be used as a random evaluation point. (labelT)
|
||||
// 5. Append point E which is sum(r^i * f_i(x)/(t-z_i)). (labelE)
|
||||
// 7. Create the IPA proof for (E-D) at point `t`. See the `ipa` package for the FS description.
|
||||
//
|
||||
// Note: this package must not mutate these label values, nor pass them to
|
||||
// parts of the code that would mutate them.
|
||||
var (
|
||||
labelC = []byte("C")
|
||||
labelZ = []byte("z")
|
||||
labelY = []byte("y")
|
||||
labelD = []byte("D")
|
||||
labelE = []byte("E")
|
||||
labelT = []byte("t")
|
||||
labelR = []byte("r")
|
||||
labelDomainSep = []byte("multiproof")
|
||||
)
|
||||
|
||||
// MultiProof is a multi-proof for several polynomials in evaluation form.
|
||||
type MultiProof struct {
|
||||
IPA ipa.IPAProof
|
||||
D banderwagon.Element
|
||||
}
|
||||
|
||||
// CreateMultiProof creates a multi-proof for several polynomials in evaluation form.
|
||||
// The list of triplets (C, Fs, Z) represents each polynomial commitment, evaluations in the domain, and evaluation
|
||||
// point respectively.
|
||||
func CreateMultiProof(transcript *common.Transcript, ipaConf *ipa.IPAConfig, Cs []*banderwagon.Element, fs [][]fr.Element, zs []uint8) (*MultiProof, error) {
|
||||
transcript.DomainSep(labelDomainSep)
|
||||
|
||||
for _, f := range fs {
|
||||
if len(f) != common.VectorLength {
|
||||
return nil, fmt.Errorf("polynomial length = %d, while expected length = %d", len(f), common.VectorLength)
|
||||
}
|
||||
}
|
||||
|
||||
if len(Cs) != len(fs) {
|
||||
return nil, fmt.Errorf("number of commitments = %d, while number of functions = %d", len(Cs), len(fs))
|
||||
}
|
||||
if len(Cs) != len(zs) {
|
||||
return nil, fmt.Errorf("number of commitments = %d, while number of points = %d", len(Cs), len(zs))
|
||||
}
|
||||
|
||||
num_queries := len(Cs)
|
||||
if num_queries == 0 {
|
||||
return nil, errors.New("cannot create a multiproof with 0 queries")
|
||||
}
|
||||
|
||||
if err := banderwagon.BatchNormalize(Cs); err != nil {
|
||||
return nil, fmt.Errorf("could not batch normalize commitments: %w", err)
|
||||
}
|
||||
|
||||
for i := 0; i < num_queries; i++ {
|
||||
transcript.AppendPoint(Cs[i], labelC)
|
||||
var z = domainToFr(zs[i])
|
||||
transcript.AppendScalar(&z, labelZ)
|
||||
|
||||
// get the `y` value
|
||||
|
||||
f := fs[i]
|
||||
y := f[zs[i]]
|
||||
transcript.AppendScalar(&y, labelY)
|
||||
}
|
||||
|
||||
r := transcript.ChallengeScalar(labelR)
|
||||
powersOfR := common.PowersOf(r, num_queries)
|
||||
|
||||
// Compute g(x)
|
||||
// We first compute the polynomials in lagrange form grouped by evaluation point, and
|
||||
// then we compute g(X). This limit the numbers of DivideOnDomain() calls up to
|
||||
// the domain size.
|
||||
groupedFs := groupPolynomialsByEvaluationPoint(fs, powersOfR, zs)
|
||||
|
||||
g_x := make([]fr.Element, common.VectorLength)
|
||||
for index, f := range groupedFs {
|
||||
// If there is no polynomial for this evaluation point, we skip it.
|
||||
if len(f) == 0 {
|
||||
continue
|
||||
}
|
||||
quotient := ipaConf.PrecomputedWeights.DivideOnDomain(uint8(index), f)
|
||||
for j := 0; j < common.VectorLength; j++ {
|
||||
g_x[j].Add(&g_x[j], "ient[j])
|
||||
}
|
||||
}
|
||||
|
||||
D := ipaConf.Commit(g_x)
|
||||
|
||||
transcript.AppendPoint(&D, labelD)
|
||||
t := transcript.ChallengeScalar(labelT)
|
||||
|
||||
// Calculate the denominator inverses only for referenced evaluation points.
|
||||
den_inv := make([]fr.Element, 0, common.VectorLength)
|
||||
for z, f := range groupedFs {
|
||||
if len(f) == 0 {
|
||||
continue
|
||||
}
|
||||
var z = domainToFr(uint8(z))
|
||||
var den fr.Element
|
||||
den.Sub(&t, &z)
|
||||
den_inv = append(den_inv, den)
|
||||
}
|
||||
den_inv = fr.BatchInvert(den_inv)
|
||||
|
||||
// Compute h(X) = g_1(X)
|
||||
h_x := make([]fr.Element, common.VectorLength)
|
||||
denInvIdx := 0
|
||||
for _, f := range groupedFs {
|
||||
if len(f) == 0 {
|
||||
continue
|
||||
}
|
||||
for k := 0; k < common.VectorLength; k++ {
|
||||
var tmp fr.Element
|
||||
tmp.Mul(&f[k], &den_inv[denInvIdx])
|
||||
h_x[k].Add(&h_x[k], &tmp)
|
||||
}
|
||||
denInvIdx++
|
||||
}
|
||||
|
||||
h_minus_g := make([]fr.Element, common.VectorLength)
|
||||
for i := 0; i < common.VectorLength; i++ {
|
||||
h_minus_g[i].Sub(&h_x[i], &g_x[i])
|
||||
}
|
||||
|
||||
E := ipaConf.Commit(h_x)
|
||||
transcript.AppendPoint(&E, labelE)
|
||||
|
||||
var EminusD banderwagon.Element
|
||||
|
||||
EminusD.Sub(&E, &D)
|
||||
|
||||
ipaProof, err := ipa.CreateIPAProof(transcript, ipaConf, EminusD, h_minus_g, t)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create IPA proof: %w", err)
|
||||
}
|
||||
|
||||
return &MultiProof{
|
||||
IPA: ipaProof,
|
||||
D: D,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CheckMultiProof verifies a multi-proof for several polynomials in evaluation form.
|
||||
// The list of triplets (C, Y, Z) represents each polynomial commitment, evaluation
|
||||
// result, and evaluation point in the domain.
|
||||
func CheckMultiProof(transcript *common.Transcript, ipaConf *ipa.IPAConfig, proof *MultiProof, Cs []*banderwagon.Element, ys []*fr.Element, zs []uint8) (bool, error) {
|
||||
transcript.DomainSep(labelDomainSep)
|
||||
|
||||
if len(Cs) != len(ys) {
|
||||
return false, fmt.Errorf("number of commitments = %d, while number of output points = %d", len(Cs), len(ys))
|
||||
}
|
||||
if len(Cs) != len(zs) {
|
||||
return false, fmt.Errorf("number of commitments = %d, while number of input points = %d", len(Cs), len(zs))
|
||||
}
|
||||
|
||||
num_queries := len(Cs)
|
||||
if num_queries == 0 {
|
||||
return false, errors.New("number of queries is zero")
|
||||
}
|
||||
|
||||
for i := 0; i < num_queries; i++ {
|
||||
transcript.AppendPoint(Cs[i], labelC)
|
||||
var z = domainToFr(zs[i])
|
||||
transcript.AppendScalar(&z, labelZ)
|
||||
transcript.AppendScalar(ys[i], labelY)
|
||||
}
|
||||
|
||||
r := transcript.ChallengeScalar(labelR)
|
||||
powers_of_r := common.PowersOf(r, num_queries)
|
||||
|
||||
transcript.AppendPoint(&proof.D, labelD)
|
||||
t := transcript.ChallengeScalar(labelT)
|
||||
|
||||
// Compute the polynomials in lagrange form grouped by evaluation point, and
|
||||
// the needed helper scalars.
|
||||
groupedEvals := make([]fr.Element, common.VectorLength)
|
||||
for i := 0; i < num_queries; i++ {
|
||||
z := zs[i]
|
||||
|
||||
// r * y_i
|
||||
r := powers_of_r[i]
|
||||
var scaledEvaluation fr.Element
|
||||
scaledEvaluation.Mul(&r, ys[i])
|
||||
groupedEvals[z].Add(&groupedEvals[z], &scaledEvaluation)
|
||||
}
|
||||
|
||||
// Compute helper_scalar_den. This is 1 / t - z_i
|
||||
helper_scalar_den := make([]fr.Element, common.VectorLength)
|
||||
for i := 0; i < common.VectorLength; i++ {
|
||||
// (t - z_i)
|
||||
var z = domainToFr(uint8(i))
|
||||
helper_scalar_den[i].Sub(&t, &z)
|
||||
}
|
||||
helper_scalar_den = fr.BatchInvert(helper_scalar_den)
|
||||
|
||||
// Compute g_2(t) = SUM (y_i * r^i) / (t - z_i) = SUM (y_i * r) * helper_scalars_den
|
||||
g_2_t := fr.Zero()
|
||||
for i := 0; i < common.VectorLength; i++ {
|
||||
if groupedEvals[i].IsZero() {
|
||||
continue
|
||||
}
|
||||
var tmp fr.Element
|
||||
tmp.Mul(&groupedEvals[i], &helper_scalar_den[i])
|
||||
g_2_t.Add(&g_2_t, &tmp)
|
||||
}
|
||||
|
||||
// Compute E = SUM C_i * (r^i / t - z_i) = SUM C_i * msm_scalars
|
||||
msm_scalars := make([]fr.Element, len(Cs))
|
||||
Csnp := make([]banderwagon.Element, len(Cs))
|
||||
for i := 0; i < len(Cs); i++ {
|
||||
Csnp[i] = *Cs[i]
|
||||
|
||||
msm_scalars[i].Mul(&powers_of_r[i], &helper_scalar_den[zs[i]])
|
||||
}
|
||||
|
||||
E, err := ipa.MultiScalar(Csnp, msm_scalars)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not compute E: %w", err)
|
||||
}
|
||||
transcript.AppendPoint(&E, labelE)
|
||||
|
||||
var E_minus_D banderwagon.Element
|
||||
E_minus_D.Sub(&E, &proof.D)
|
||||
|
||||
ok, err := ipa.CheckIPAProof(transcript, ipaConf, E_minus_D, proof.IPA, t, g_2_t)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("could not check IPA proof: %w", err)
|
||||
}
|
||||
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func domainToFr(in uint8) fr.Element {
|
||||
var x fr.Element
|
||||
x.SetUint64(uint64(in))
|
||||
return x
|
||||
}
|
||||
|
||||
// Write serializes a multi-proof to a writer.
|
||||
func (mp *MultiProof) Write(w io.Writer) error {
|
||||
if err := binary.Write(w, binary.BigEndian, mp.D.Bytes()); err != nil {
|
||||
return fmt.Errorf("failed to write D: %w", err)
|
||||
}
|
||||
if err := mp.IPA.Write(w); err != nil {
|
||||
return fmt.Errorf("failed to write IPA proof: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read deserializes a multi-proof from a reader.
|
||||
func (mp *MultiProof) Read(r io.Reader) error {
|
||||
D, err := common.ReadPoint(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read D: %w", err)
|
||||
}
|
||||
mp.D = *D
|
||||
if err := mp.IPA.Read(r); err != nil {
|
||||
return fmt.Errorf("failed to read IPA proof: %w", err)
|
||||
}
|
||||
// Check that the next read is EOF.
|
||||
var buf [1]byte
|
||||
if _, err := r.Read(buf[:]); err != io.EOF {
|
||||
return errors.New("expected EOF")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Equal checks if two multi-proofs are equal.
|
||||
func (mp MultiProof) Equal(other MultiProof) bool {
|
||||
if !mp.IPA.Equal(other.IPA) {
|
||||
return false
|
||||
}
|
||||
return mp.D.Equal(&other.D)
|
||||
}
|
||||
|
||||
func groupPolynomialsByEvaluationPoint(fs [][]fr.Element, powersOfR []fr.Element, zs []uint8) [common.VectorLength][]fr.Element {
|
||||
workersAggregations := make(chan [common.VectorLength][]fr.Element)
|
||||
|
||||
numWorkers := runtime.NumCPU()
|
||||
batchSize := (len(fs) + numWorkers - 1) / numWorkers
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
go func(start, end int) {
|
||||
if end > len(fs) {
|
||||
end = len(fs)
|
||||
}
|
||||
var groupedFs [common.VectorLength][]fr.Element
|
||||
for i := start; i < end; i++ {
|
||||
z := zs[i]
|
||||
if len(groupedFs[z]) == 0 {
|
||||
groupedFs[z] = make([]fr.Element, common.VectorLength)
|
||||
}
|
||||
|
||||
for j := 0; j < common.VectorLength; j++ {
|
||||
var scaledEvaluation fr.Element
|
||||
scaledEvaluation.Mul(&powersOfR[i], &fs[i][j])
|
||||
groupedFs[z][j].Add(&groupedFs[z][j], &scaledEvaluation)
|
||||
}
|
||||
}
|
||||
workersAggregations <- groupedFs
|
||||
}(i*batchSize, (i+1)*batchSize)
|
||||
}
|
||||
|
||||
// Each worker has computed its own aggregation. Now we aggregate the results.
|
||||
// This is bounded to reducing a `numWorkers` sized array of `common.VectorLength` sized arrays.
|
||||
var groupedFs [common.VectorLength][]fr.Element
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
workerAggregation := <-workersAggregations
|
||||
for z := range workerAggregation {
|
||||
if len(workerAggregation[z]) == 0 {
|
||||
continue
|
||||
}
|
||||
// If this is the first time we see this evaluation point, we initialize it
|
||||
// reusing the worker result.
|
||||
if groupedFs[z] == nil {
|
||||
groupedFs[z] = workerAggregation[z]
|
||||
continue
|
||||
}
|
||||
// If not, we aggregate the worker result with the previous result for this evaluation.
|
||||
for j := 0; j < common.VectorLength; j++ {
|
||||
groupedFs[z][j].Add(&groupedFs[z][j], &workerAggregation[z][j])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return groupedFs
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
package multiproof
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
"github.com/luxfi/crypto/ipa/common"
|
||||
"github.com/luxfi/crypto/ipa/ipa"
|
||||
"github.com/luxfi/crypto/ipa/test_helper"
|
||||
)
|
||||
|
||||
var ipaConf *ipa.IPAConfig
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
ipaConf, err = ipa.NewIPASettings()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func TestMultiProofCreateVerify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Prover view
|
||||
poly_1 := test_helper.TestPoly256(1, 1, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
|
||||
prover_transcript := common.NewTranscript("multiproof")
|
||||
prover_comm_1 := ipaConf.Commit(poly_1)
|
||||
|
||||
one := fr.One()
|
||||
|
||||
Cs := []*banderwagon.Element{&prover_comm_1}
|
||||
fs := [][]fr.Element{poly_1}
|
||||
zs := []uint8{0}
|
||||
ys := []*fr.Element{&one}
|
||||
proof, err := CreateMultiProof(prover_transcript, ipaConf, Cs, fs, zs)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create multiproof: %s", err)
|
||||
}
|
||||
|
||||
test_serialize_deserialize_proof(t, *proof)
|
||||
|
||||
// Verifier view
|
||||
verifier_transcript := common.NewTranscript("multiproof")
|
||||
ok, err := CheckMultiProof(verifier_transcript, ipaConf, proof, Cs, ys, zs)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to verify multiproof: %s", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("failed to verify multiproof")
|
||||
}
|
||||
|
||||
}
|
||||
func TestMultiProofConsistency(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Prover view
|
||||
poly_a := test_helper.TestPoly256(
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
|
||||
)
|
||||
poly_b := test_helper.TestPoly256(
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
|
||||
)
|
||||
prover_transcript := common.NewTranscript("test")
|
||||
comm_a := ipaConf.Commit(poly_a)
|
||||
comm_b := ipaConf.Commit(poly_b)
|
||||
|
||||
one := fr.One()
|
||||
var thirty_two = fr.Element{}
|
||||
thirty_two.SetUint64(32)
|
||||
|
||||
Cs := []*banderwagon.Element{&comm_a, &comm_b}
|
||||
fs := [][]fr.Element{poly_a, poly_b}
|
||||
zs := []uint8{0, 0}
|
||||
ys := []*fr.Element{&one, &thirty_two}
|
||||
|
||||
proof, err := CreateMultiProof(prover_transcript, ipaConf, Cs, fs, zs)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create multiproof: %s", err)
|
||||
}
|
||||
|
||||
// Lets check the state of the transcript, by squeezing out a challenge
|
||||
p_challenge := prover_transcript.ChallengeScalar([]byte("state"))
|
||||
test_helper.ScalarEqualHex(t, p_challenge, "eee8a80357ff74b766eba39db90797d022e8d6dee426ded71234241be504d519")
|
||||
|
||||
// Verifier view
|
||||
verifier_transcript := common.NewTranscript("test")
|
||||
ok, err := CheckMultiProof(verifier_transcript, ipaConf, proof, Cs, ys, zs)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to verify multiproof: %s", err)
|
||||
}
|
||||
|
||||
if !ok {
|
||||
t.Fatal("failed to verify multiproof")
|
||||
}
|
||||
|
||||
// Check serialised bytes are consistent with other implementations
|
||||
expected := "4f53588244efaf07a370ee3f9c467f933eed360d4fbf7a19dfc8bc49b67df4711bf1d0a720717cd6a8c75f1a668cb7cbdd63b48c676b89a7aee4298e71bd7f4013d7657146aa9736817da47051ed6a45fc7b5a61d00eb23e5df82a7f285cc10e67d444e91618465ca68d8ae4f2c916d1942201b7e2aae491ef0f809867d00e83468fb7f9af9b42ede76c1e90d89dd789ff22eb09e8b1d062d8a58b6f88b3cbe80136fc68331178cd45a1df9496ded092d976911b5244b85bc3de41e844ec194256b39aeee4ea55538a36139211e9910ad6b7a74e75d45b869d0a67aa4bf600930a5f760dfb8e4df9938d1f47b743d71c78ba8585e3b80aba26d24b1f50b36fa1458e79d54c05f58049245392bc3e2b5c5f9a1b99d43ed112ca82b201fb143d401741713188e47f1d6682b0bf496a5d4182836121efff0fd3b030fc6bfb5e21d6314a200963fe75cb856d444a813426b2084dfdc49dca2e649cb9da8bcb47859a4c629e97898e3547c591e39764110a224150d579c33fb74fa5eb96427036899c04154feab5344873d36a53a5baefd78c132be419f3f3a8dd8f60f72eb78dd5f43c53226f5ceb68947da3e19a750d760fb31fa8d4c7f53bfef11c4b89158aa56b1f4395430e16a3128f88e234ce1df7ef865f2d2c4975e8c82225f578310c31fd41d265fd530cbfa2b8895b228a510b806c31dff3b1fa5c08bffad443d567ed0e628febdd22775776e0cc9cebcaea9c6df9279a5d91dd0ee5e7a0434e989a160005321c97026cb559f71db23360105460d959bcdf74bee22c4ad8805a1d497507"
|
||||
|
||||
var buf = new(bytes.Buffer)
|
||||
if err := proof.Write(buf); err != nil {
|
||||
t.Fatalf("failed to write proof: %s", err)
|
||||
}
|
||||
|
||||
bytes := buf.Bytes()
|
||||
if expected != hex.EncodeToString(bytes) {
|
||||
t.Fatalf("expected serialised proof is different from the other implementations")
|
||||
}
|
||||
}
|
||||
|
||||
func test_serialize_deserialize_proof(t *testing.T, proof MultiProof) {
|
||||
var buf = new(bytes.Buffer)
|
||||
if err := proof.Write(buf); err != nil {
|
||||
t.Fatalf("failed to write proof: %s", err)
|
||||
}
|
||||
|
||||
var got_proof MultiProof
|
||||
if err := got_proof.Read(buf); err != nil {
|
||||
t.Fatalf("failed to read proof: %s", err)
|
||||
}
|
||||
|
||||
if !got_proof.Equal(proof) {
|
||||
t.Fatal("proof serialization does not match deserialization for Multiproof")
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzMultiProofCreateVerify(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, p0_z uint8, p0_0, p0_1, p0_2, p0_3, p0_4, p0_5, p0_6, p0_7, p0_8, p0_9, p0_10 uint64) {
|
||||
if p0_z > 10 {
|
||||
return
|
||||
}
|
||||
|
||||
poly_1 := test_helper.TestPoly256(p0_0, p0_1, p0_2, p0_3, p0_4, p0_5, p0_6, p0_7, p0_8, p0_9, p0_10)
|
||||
prover_transcript := common.NewTranscript("multiproof")
|
||||
prover_comm_1 := ipaConf.Commit(poly_1)
|
||||
|
||||
Cs := []*banderwagon.Element{&prover_comm_1}
|
||||
fs := [][]fr.Element{poly_1}
|
||||
zs := []uint8{p0_z}
|
||||
proof, err := CreateMultiProof(prover_transcript, ipaConf, Cs, fs, zs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create multiproof: %s", err)
|
||||
}
|
||||
|
||||
test_serialize_deserialize_proof(t, *proof)
|
||||
|
||||
// Verifier view
|
||||
verifier_transcript := common.NewTranscript("multiproof")
|
||||
|
||||
ys := []*fr.Element{&poly_1[p0_z]}
|
||||
ok, err := CheckMultiProof(verifier_transcript, ipaConf, proof, Cs, ys, zs)
|
||||
if err != nil && ok {
|
||||
t.Fatalf("failing to verify multiproof can't return OK: %s", err)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("failed to verify multiproof")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzMultiProofCreateVerifyOpaqueBytes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, z uint8, evalPointsBytes []byte) {
|
||||
if len(evalPointsBytes) != common.VectorLength*32 {
|
||||
return
|
||||
}
|
||||
evalPoints := make([]fr.Element, len(evalPointsBytes)/32)
|
||||
for i := 0; i < len(evalPointsBytes); i += 32 {
|
||||
evalPoint := evalPointsBytes[i : i+32]
|
||||
if err := evalPoints[i/64].SetBytes(evalPoint); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
poly_1 := evalPoints
|
||||
prover_transcript := common.NewTranscript("multiproof")
|
||||
prover_comm_1 := ipaConf.Commit(poly_1)
|
||||
|
||||
Cs := []*banderwagon.Element{&prover_comm_1}
|
||||
fs := [][]fr.Element{poly_1}
|
||||
zs := []uint8{z}
|
||||
proof, err := CreateMultiProof(prover_transcript, ipaConf, Cs, fs, zs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
test_serialize_deserialize_proof(t, *proof)
|
||||
|
||||
// Verifier view
|
||||
verifier_transcript := common.NewTranscript("multiproof")
|
||||
|
||||
ys := []*fr.Element{&poly_1[z]}
|
||||
ok, err := CheckMultiProof(verifier_transcript, ipaConf, proof, Cs, ys, zs)
|
||||
if err != nil && ok {
|
||||
t.Fatalf("failing to verify multiproof can't return OK: %s", err)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("failed to verify multiproof")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzMultiProofDeserialize(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, proofBytes []byte) {
|
||||
var proof MultiProof
|
||||
err := proof.Read(bytes.NewReader(proofBytes))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var buf = new(bytes.Buffer)
|
||||
if err := proof.Write(buf); err != nil {
|
||||
t.Fatalf("failed to write proof: %s", err)
|
||||
}
|
||||
a := buf.Bytes()
|
||||
if !bytes.Equal(a, proofBytes) {
|
||||
t.Fatalf("proof serialization does not match deserialization for Multiproof")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkProofGeneration(b *testing.B) {
|
||||
numOpenings := []int{2_000, 16_000, 32_000, 64_000, 128_000}
|
||||
openings := genRandomPolynomialOpenings(b, numOpenings[len(numOpenings)-1])
|
||||
|
||||
for _, n := range numOpenings {
|
||||
b.Run(fmt.Sprintf("numopenings=%d", n), func(b *testing.B) {
|
||||
Cs := make([]*banderwagon.Element, n)
|
||||
fs := make([][]fr.Element, n)
|
||||
zs := make([]uint8, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
Cs[i] = &openings[i].commitment
|
||||
fs[i] = openings[i].evaluations[:]
|
||||
zs[i] = openings[i].evalPoint
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr := common.NewTranscript("multiproof")
|
||||
|
||||
b.StopTimer()
|
||||
Cs2 := make([]*banderwagon.Element, n)
|
||||
for i := 0; i < n; i++ {
|
||||
cs2 := *Cs[i]
|
||||
Cs2[i] = &cs2
|
||||
}
|
||||
b.StartTimer()
|
||||
|
||||
if _, err := CreateMultiProof(tr, ipaConf, Cs2, fs, zs); err != nil {
|
||||
b.Fatalf("failed to create multiproof: %s", err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkProofVerification(b *testing.B) {
|
||||
numOpenings := []int{2_000, 16_000, 32_000, 64_000, 128_000}
|
||||
openings := genRandomPolynomialOpenings(b, numOpenings[len(numOpenings)-1])
|
||||
|
||||
for _, n := range numOpenings {
|
||||
b.Run(fmt.Sprintf("numopenings=%d", n), func(b *testing.B) {
|
||||
Cs := make([]*banderwagon.Element, n)
|
||||
fs := make([][]fr.Element, n)
|
||||
zs := make([]uint8, n)
|
||||
ys := make([]*fr.Element, n)
|
||||
for i := 0; i < n; i++ {
|
||||
Cs[i] = &openings[i].commitment
|
||||
fs[i] = openings[i].evaluations[:]
|
||||
zs[i] = openings[i].evalPoint
|
||||
ys[i] = &fs[i][zs[i]]
|
||||
}
|
||||
transcriptProving := common.NewTranscript("multiproof")
|
||||
proof, err := CreateMultiProof(transcriptProving, ipaConf, Cs, fs, zs)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create multiproof: %s", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tr := common.NewTranscript("multiproof")
|
||||
if ok, err := CheckMultiProof(tr, ipaConf, proof, Cs, ys, zs); !ok || err != nil {
|
||||
b.Fatalf("failed to verify multiproof")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func genRandomPolynomialOpenings(t testing.TB, n int) []polyOpening {
|
||||
openings := make([]polyOpening, n)
|
||||
|
||||
batches := runtime.NumCPU()
|
||||
batchSize := (n + batches - 1) / batches
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(batches)
|
||||
for i := 0; i < batches; i++ {
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < batchSize; j++ {
|
||||
if i*batchSize+j >= n {
|
||||
break
|
||||
}
|
||||
openings[i*batchSize+j] = genRandomPolynomialOpening(t)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return openings
|
||||
}
|
||||
|
||||
type polyOpening struct {
|
||||
commitment banderwagon.Element
|
||||
evaluations [256]fr.Element
|
||||
evalPoint uint8
|
||||
}
|
||||
|
||||
func genRandomPolynomialOpening(t testing.TB) polyOpening {
|
||||
var polynomialFr [256]fr.Element
|
||||
for i := range polynomialFr {
|
||||
if _, err := polynomialFr[i].SetRandom(); err != nil {
|
||||
t.Fatalf("failed to set random element: %s", err)
|
||||
}
|
||||
}
|
||||
c := ipaConf.Commit(polynomialFr[:])
|
||||
|
||||
return polyOpening{
|
||||
commitment: c,
|
||||
evaluations: polynomialFr,
|
||||
evalPoint: uint8(rand.Uint32()),
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package test_helper
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/bandersnatch/fr"
|
||||
)
|
||||
|
||||
func TestPoly256(polynomial ...uint64) []fr.Element {
|
||||
n := len(polynomial)
|
||||
if len(polynomial) > 256 {
|
||||
panic("polynomial cannot exceed 256 coefficients")
|
||||
}
|
||||
polynomialFr := make([]fr.Element, 256)
|
||||
for i := 0; i < n; i++ {
|
||||
polynomialFr[i].SetUint64(polynomial[i])
|
||||
}
|
||||
|
||||
pad := 256 - n
|
||||
for i := n; i < pad; i++ {
|
||||
polynomialFr[i] = fr.Zero()
|
||||
}
|
||||
|
||||
return polynomialFr
|
||||
}
|
||||
|
||||
func PointEqualHex(t *testing.T, point banderwagon.Element, expected string) {
|
||||
point_bytes := point.Bytes()
|
||||
got := hex.EncodeToString(point_bytes[:])
|
||||
if got != expected {
|
||||
t.Fatalf("point does not equal expected hex value, expected %s got %s", expected, got)
|
||||
}
|
||||
}
|
||||
func ScalarEqualHex(t *testing.T, scalar fr.Element, expected string) {
|
||||
scalar_bytes := scalar.BytesLE()
|
||||
got := hex.EncodeToString(scalar_bytes[:])
|
||||
if got != expected {
|
||||
t.Fatalf("scalar does not equal expected hex value, expected %s got %s", expected, got)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("20A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C8109720A820010b217a2B70Ab010878C810970201001111020B01002011002222000000000000000000000000000000000000")
|
||||
@@ -1,2 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("77Z101c210210c7290Z112717120c020")
|
||||
@@ -1,2 +0,0 @@
|
||||
go test fuzz v1
|
||||
[]byte("10110180102007200002000C0b201B282080A87Y80A021YY8722071ACA2YA10810121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A10110121BX0120801aZ829A170170Y0A101000000000000000000000000000000000")
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
"runtime"
|
||||
"sort"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
)
|
||||
|
||||
func TestParseNodeEmptyPayload(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ package verkle
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ import (
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/crypto/banderwagon"
|
||||
"github.com/luxfi/crypto/ipa/banderwagon"
|
||||
)
|
||||
|
||||
var identity *Point
|
||||
|
||||
Reference in New Issue
Block a user