diff --git a/internal/tinyfield/arith.go b/internal/tinyfield/arith.go index 02aedba1f..2b85fa7ab 100644 --- a/internal/tinyfield/arith.go +++ b/internal/tinyfield/arith.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT diff --git a/internal/tinyfield/doc.go b/internal/tinyfield/doc.go index a8b6fce69..3e92316c8 100644 --- a/internal/tinyfield/doc.go +++ b/internal/tinyfield/doc.go @@ -1,11 +1,13 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT // Package tinyfield contains field arithmetic operations for modulus = 0x2f. // -// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x for the modular multiplication on amd64, see also https://hackmd.io/@gnark/modular_multiplication) +// The API is similar to math/big (big.Int), but the operations are significantly faster (up to 20x). +// +// Additionally tinyfield.Vector offers an API to manipulate []Element. // // The modulus is hardcoded in all the operations. // @@ -38,5 +40,7 @@ // // # Warning // -// This code has not been audited and is provided as-is. In particular, there is no security guarantees such as constant time implementation or side-channel attack resistance. +// There is no security guarantees such as constant time implementation or side-channel attack resistance. +// This code is provided as-is. Partially audited, see https://github.com/Consensys/gnark/tree/master/audits +// for more details. package tinyfield diff --git a/internal/tinyfield/element.go b/internal/tinyfield/element.go index 5d7e45ae3..497fabfa9 100644 --- a/internal/tinyfield/element.go +++ b/internal/tinyfield/element.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT @@ -43,8 +43,8 @@ const ( // Field modulus q const ( - q0 uint64 = 47 - q uint64 = q0 + q0 = 47 + q = q0 ) var qElement = Element{ @@ -63,7 +63,7 @@ func Modulus() *big.Int { // q + r'.r = 1, i.e., qInvNeg = - q⁻¹ mod r // used for Montgomery reduction -const qInvNeg uint64 = 12559485326780971313 +const qInvNeg = 12559485326780971313 func init() { _modulus.SetString("2f", 16) @@ -338,10 +338,11 @@ func (z *Element) fromMont() *Element { // Add z = x + y (mod q) func (z *Element) Add(x, y *Element) *Element { - z[0], _ = bits.Add64(x[0], y[0], 0) - if z[0] >= q { - z[0] -= q + t := x[0] + y[0] + if t >= q { + t -= q } + z[0] = t return z } @@ -388,49 +389,6 @@ func (z *Element) Select(c int, x0 *Element, x1 *Element) *Element { return z } -// _mulGeneric is unoptimized textbook CIOS -// it is a fallback solution on x86 when ADX instruction set is not available -// and is used for testing purposes. -func _mulGeneric(z, x, y *Element) { - - // Algorithm 2 of "Faster Montgomery Multiplication and Multi-Scalar-Multiplication for SNARKS" - // by Y. El Housni and G. Botrel https://doi.org/10.46586/tches.v2023.i3.504-521 - - var t [2]uint64 - var D uint64 - var m, C uint64 - // ----------------------------------- - // First loop - - C, t[0] = bits.Mul64(y[0], x[0]) - - t[1], D = bits.Add64(t[1], C, 0) - - // m = t[0]n'[0] mod W - m = t[0] * qInvNeg - - // ----------------------------------- - // Second loop - C = madd0(m, q0, t[0]) - - t[0], C = bits.Add64(t[1], C, 0) - t[1], _ = bits.Add64(0, D, C) - - if t[1] != 0 { - // we need to reduce, we have a result on 2 words - z[0], _ = bits.Sub64(t[0], q0, 0) - return - } - - // copy t into z - z[0] = t[0] - - // if z ⩾ q → z -= q - if !z.smallerThanModulus() { - z[0] -= q - } -} - func _fromMontGeneric(z *Element) { // the following lines implement z = z * 1 // with a modified CIOS montgomery multiplication @@ -603,7 +561,7 @@ func (z *Element) Text(base int) string { const maxUint16 = 65535 zz := z.Bits() - return strconv.FormatUint(zz[0], base) + return strconv.FormatUint(uint64(zz[0]), base) } // BigInt sets and return z as a *big.Int diff --git a/internal/tinyfield/element_ops_purego.go b/internal/tinyfield/element_purego.go similarity index 97% rename from internal/tinyfield/element_ops_purego.go rename to internal/tinyfield/element_purego.go index 609d9cbd7..eefc24dab 100644 --- a/internal/tinyfield/element_ops_purego.go +++ b/internal/tinyfield/element_purego.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT @@ -28,14 +28,6 @@ func MulBy13(x *Element) { x.Mul(x, &y) } -// Butterfly sets -// -// a = a + b (mod q) -// b = a - b (mod q) -func Butterfly(a, b *Element) { - _butterflyGeneric(a, b) -} - func fromMont(z *Element) { _fromMontGeneric(z) } @@ -61,7 +53,6 @@ func (z *Element) Mul(x, y *Element) *Element { // Which finally gives (lo + m * q) / R = (lo + lo2 + R hi2) / R = hi2 + (lo+lo2) / R = hi2 + (lo != 0) // This "optimization" lets us do away with one MUL instruction on ARM architectures and is available for all q < R. - var r uint64 hi, lo := bits.Mul64(x[0], y[0]) if lo != 0 { hi++ // x[0] * y[0] ≤ 2¹²⁸ - 2⁶⁵ + 1, meaning hi ≤ 2⁶⁴ - 2 so no need to worry about overflow @@ -69,7 +60,6 @@ func (z *Element) Mul(x, y *Element) *Element { m := lo * qInvNeg hi2, _ := bits.Mul64(m, q) r, carry := bits.Add64(hi2, hi, 0) - if carry != 0 || r >= q { // we need to reduce r -= q @@ -97,7 +87,6 @@ func (z *Element) Square(x *Element) *Element { // Which finally gives (lo + m * q) / R = (lo + lo2 + R hi2) / R = hi2 + (lo+lo2) / R = hi2 + (lo != 0) // This "optimization" lets us do away with one MUL instruction on ARM architectures and is available for all q < R. - var r uint64 hi, lo := bits.Mul64(x[0], x[0]) if lo != 0 { hi++ // x[0] * y[0] ≤ 2¹²⁸ - 2⁶⁵ + 1, meaning hi ≤ 2⁶⁴ - 2 so no need to worry about overflow @@ -105,7 +94,6 @@ func (z *Element) Square(x *Element) *Element { m := lo * qInvNeg hi2, _ := bits.Mul64(m, q) r, carry := bits.Add64(hi2, hi, 0) - if carry != 0 || r >= q { // we need to reduce r -= q @@ -114,3 +102,11 @@ func (z *Element) Square(x *Element) *Element { return z } + +// Butterfly sets +// +// a = a + b (mod q) +// b = a - b (mod q) +func Butterfly(a, b *Element) { + _butterflyGeneric(a, b) +} diff --git a/internal/tinyfield/element_test.go b/internal/tinyfield/element_test.go index 64d9667a5..cece30c91 100644 --- a/internal/tinyfield/element_test.go +++ b/internal/tinyfield/element_test.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT @@ -921,14 +921,6 @@ func TestElementMul(t *testing.T) { c.Mul(&a.element, &r) d.Mul(&a.bigint, &rb).Mod(&d, Modulus()) - // checking generic impl against asm path - var cGeneric Element - _mulGeneric(&cGeneric, &a.element, &r) - if !cGeneric.Equal(&c) { - // need to give context to failing error. - return false - } - if c.BigInt(&e).Cmp(&d) != 0 { return false } @@ -951,17 +943,6 @@ func TestElementMul(t *testing.T) { genB, )) - properties.Property("Mul: assembly implementation must be consistent with generic one", prop.ForAll( - func(a, b testPairElement) bool { - var c, d Element - c.Mul(&a.element, &b.element) - _mulGeneric(&d, &a.element, &b.element) - return c.Equal(&d) - }, - genA, - genB, - )) - specialValueTest := func() { // test special values against special values testValues := make([]Element, len(staticTestValues)) @@ -980,13 +961,6 @@ func TestElementMul(t *testing.T) { c.Mul(&a, &b) d.Mul(&aBig, &bBig).Mod(&d, Modulus()) - // checking asm against generic impl - var cGeneric Element - _mulGeneric(&cGeneric, &a, &b) - if !cGeneric.Equal(&c) { - t.Fatal("Mul failed special test values: asm and generic impl don't match") - } - if c.BigInt(&e).Cmp(&d) != 0 { t.Fatal("Mul failed special test values") } diff --git a/internal/tinyfield/vector.go b/internal/tinyfield/vector.go index 6b045db8c..0439c558c 100644 --- a/internal/tinyfield/vector.go +++ b/internal/tinyfield/vector.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT @@ -185,43 +185,6 @@ func (vector Vector) Swap(i, j int) { vector[i], vector[j] = vector[j], vector[i] } -// Add adds two vectors element-wise and stores the result in self. -// It panics if the vectors don't have the same length. -func (vector *Vector) Add(a, b Vector) { - addVecGeneric(*vector, a, b) -} - -// Sub subtracts two vectors element-wise and stores the result in self. -// It panics if the vectors don't have the same length. -func (vector *Vector) Sub(a, b Vector) { - subVecGeneric(*vector, a, b) -} - -// ScalarMul multiplies a vector by a scalar element-wise and stores the result in self. -// It panics if the vectors don't have the same length. -func (vector *Vector) ScalarMul(a Vector, b *Element) { - scalarMulVecGeneric(*vector, a, b) -} - -// Sum computes the sum of all elements in the vector. -func (vector *Vector) Sum() (res Element) { - sumVecGeneric(&res, *vector) - return -} - -// InnerProduct computes the inner product of two vectors. -// It panics if the vectors don't have the same length. -func (vector *Vector) InnerProduct(other Vector) (res Element) { - innerProductVecGeneric(&res, *vector, other) - return -} - -// Mul multiplies two vectors element-wise and stores the result in self. -// It panics if the vectors don't have the same length. -func (vector *Vector) Mul(a, b Vector) { - mulVecGeneric(*vector, a, b) -} - func addVecGeneric(res, a, b Vector) { if len(a) != len(b) || len(a) != len(res) { panic("vector.Add: vectors don't have the same length") diff --git a/internal/tinyfield/vector_purego.go b/internal/tinyfield/vector_purego.go new file mode 100644 index 000000000..22a2964d1 --- /dev/null +++ b/internal/tinyfield/vector_purego.go @@ -0,0 +1,43 @@ +// Copyright 2020-2024 Consensys Software Inc. +// Licensed under the Apache License, Version 2.0. See the LICENSE file for details. + +// Code generated by consensys/gnark-crypto DO NOT EDIT + +package tinyfield + +// Add adds two vectors element-wise and stores the result in self. +// It panics if the vectors don't have the same length. +func (vector *Vector) Add(a, b Vector) { + addVecGeneric(*vector, a, b) +} + +// Sub subtracts two vectors element-wise and stores the result in self. +// It panics if the vectors don't have the same length. +func (vector *Vector) Sub(a, b Vector) { + subVecGeneric(*vector, a, b) +} + +// ScalarMul multiplies a vector by a scalar element-wise and stores the result in self. +// It panics if the vectors don't have the same length. +func (vector *Vector) ScalarMul(a Vector, b *Element) { + scalarMulVecGeneric(*vector, a, b) +} + +// Sum computes the sum of all elements in the vector. +func (vector *Vector) Sum() (res Element) { + sumVecGeneric(&res, *vector) + return +} + +// InnerProduct computes the inner product of two vectors. +// It panics if the vectors don't have the same length. +func (vector *Vector) InnerProduct(other Vector) (res Element) { + innerProductVecGeneric(&res, *vector, other) + return +} + +// Mul multiplies two vectors element-wise and stores the result in self. +// It panics if the vectors don't have the same length. +func (vector *Vector) Mul(a, b Vector) { + mulVecGeneric(*vector, a, b) +} diff --git a/internal/tinyfield/vector_test.go b/internal/tinyfield/vector_test.go index d17149d30..82867cee9 100644 --- a/internal/tinyfield/vector_test.go +++ b/internal/tinyfield/vector_test.go @@ -1,4 +1,4 @@ -// Copyright 2020-2024 ConsenSys Software Inc. +// Copyright 2020-2024 Consensys Software Inc. // Licensed under the Apache License, Version 2.0. See the LICENSE file for details. // Code generated by consensys/gnark-crypto DO NOT EDIT