github.com/consensys/gnark-crypto@v0.14.0/field/generator/config/field_test.go (about)

     1  package config
     2  
     3  import (
     4  	"crypto/rand"
     5  	"fmt"
     6  	"math/big"
     7  	"math/bits"
     8  	mrand "math/rand"
     9  	"testing"
    10  
    11  	"github.com/leanovate/gopter/gen"
    12  
    13  	"github.com/leanovate/gopter"
    14  	"github.com/leanovate/gopter/prop"
    15  )
    16  
    17  //TODO: Use genF.Map to generate ints in field instead of using byte slices
    18  
    19  func TestIntToMont(t *testing.T) {
    20  	t.Parallel()
    21  
    22  	parameters := gopter.DefaultTestParameters()
    23  	parameters.MinSuccessfulTests = 10
    24  	properties := gopter.NewProperties(parameters)
    25  	genF := genField(t)
    26  
    27  	properties.Property("must recover initial non-montgomery value by repeated halving", prop.ForAll(
    28  		func(f *FieldConfig, ib [][]uint8) (bool, error) {
    29  
    30  			var i big.Int
    31  			i.SetBytes(ib[0])
    32  			i.Mod(&i, f.ModulusBig)
    33  
    34  			// turn into mont
    35  			mont := f.ToMont(i)
    36  			f.FromMont(&mont, &mont)
    37  
    38  			return mont.Cmp(&i) == 0, nil
    39  		}, genF, genUint8SliceSlice(1),
    40  	))
    41  
    42  	properties.Property("turning R into montgomery form must match the R value from field", prop.ForAll(
    43  		func(f *FieldConfig) (bool, error) {
    44  			// test if using the same R
    45  			i := big.NewInt(1)
    46  			i.Lsh(i, 64*uint(f.NbWords))
    47  			*i = f.ToMont(*i)
    48  
    49  			err := bigIntMatchUint64Slice(i, f.RSquare)
    50  			return err == nil, err
    51  		}, genF),
    52  	)
    53  
    54  	properties.TestingRun(t, gopter.ConsoleReporter(false))
    55  }
    56  
    57  func TestBigIntMatchUint64Slice(t *testing.T) {
    58  	t.Parallel()
    59  
    60  	parameters := gopter.DefaultTestParameters()
    61  	parameters.MinSuccessfulTests = 10
    62  	properties := gopter.NewProperties(parameters)
    63  	genF := genField(t)
    64  
    65  	properties.Property("random big.int must match uint64 slice made out of .Bytes()", prop.ForAll(
    66  		func(f *FieldConfig, ib [][]uint8) (bool, error) {
    67  
    68  			var i big.Int
    69  			i.SetBytes(ib[0])
    70  			bytes := i.Bytes()
    71  			ints := make([]uint64, (len(bytes)-1)/8+1)
    72  
    73  			for j := 0; j < len(bytes); j++ {
    74  				ints[j/8] ^= uint64(bytes[len(bytes)-1-j]) << (8 * (j % 8))
    75  			}
    76  
    77  			err := bigIntMatchUint64Slice(&i, ints)
    78  			return err == nil, err
    79  		}, genF, genUint8SliceSlice(1)))
    80  
    81  	properties.TestingRun(t, gopter.ConsoleReporter(false))
    82  }
    83  
    84  func TestQuadExtensionMul(t *testing.T) {
    85  	t.Parallel()
    86  
    87  	verifyMul := func(base *FieldConfig, x8Slice [][]uint8, y8Slice [][]uint8) (bool, error) {
    88  		var nonRes big.Int
    89  		base.FromMont(&nonRes, &base.NonResidue)
    90  		if !nonRes.IsInt64() {
    91  			return false, fmt.Errorf("non-residue too large: %v", nonRes)
    92  		}
    93  
    94  		f := NewTower(base, 2, base.NonResidue.Int64())
    95  		x := uint8SliceSliceToBigIntSlice(&f, x8Slice)
    96  		y := uint8SliceSliceToBigIntSlice(&f, y8Slice)
    97  
    98  		z := f.Mul(x, y)
    99  
   100  		var z0, z1, u big.Int
   101  
   102  		base.
   103  			Mul(&z0, &x[0], &y[0]).
   104  			Mul(&u, &x[1], &y[1]).
   105  			Mul(&u, &u, big.NewInt(base.NonResidue.Int64())).
   106  			Add(&z0, &z0, &u)
   107  
   108  		base.
   109  			Mul(&z1, &x[0], &y[1]).
   110  			Mul(&u, &x[1], &y[0]).
   111  			Add(&z1, &z1, &u)
   112  
   113  		return z0.Cmp(&z[0]) == 0 && z1.Cmp(&z[1]) == 0, nil
   114  	}
   115  	genF := genField(t)
   116  	parameters := gopter.DefaultTestParameters()
   117  
   118  	parameters.MinSuccessfulTests = 10
   119  	properties := gopter.NewProperties(parameters)
   120  	properties.Property("multiplication should yield the correct value", prop.ForAll(verifyMul, genF, genUint8SliceSlice(2), genUint8SliceSlice(2)))
   121  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   122  
   123  	parameters.MinSuccessfulTests = 4
   124  	properties = gopter.NewProperties(parameters)
   125  	properties.Property("multiplication should yield the correct value (small cases)", prop.ForAll(
   126  		verifyMul,
   127  		genF,
   128  		genSmallUint8SliceSlice(2, 3),
   129  		genSmallUint8SliceSlice(2, 3),
   130  	))
   131  	properties.TestingRun(t, gopter.ConsoleReporter(false))
   132  }
   133  
   134  func TestExponentiationBls12381G2(t *testing.T) {
   135  	t.Parallel()
   136  
   137  	base, err := NewFieldConfig("dummyName", "dummyElement", "0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab", false)
   138  
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	f := NewTower(base, 2, -1)
   144  	Z := make([]big.Int, 2)
   145  	Z[0].SetInt64(-2)
   146  	Z[1].SetInt64(-1)
   147  
   148  	type expTestCase struct {
   149  		pow *big.Int
   150  		res []big.Int
   151  	}
   152  
   153  	cases := []expTestCase{
   154  		{big.NewInt(2), f.FromInt64(3, 4)},
   155  	}
   156  	for _, c := range cases {
   157  		res := f.Exp(Z, c.pow)
   158  		if !f.Equal(c.res, res) {
   159  			t.Error("Failed on power", c.pow.Int64())
   160  		}
   161  	}
   162  }
   163  
   164  const minNbWords = 1
   165  const maxNbWords = 15
   166  
   167  func genSmallUint8SliceSlice(outerSize int, max uint8) gopter.Gen {
   168  	return gen.SliceOfN(
   169  		outerSize,
   170  		gen.SliceOfN(1, gen.UInt8Range(0, max)),
   171  	)
   172  }
   173  
   174  func genUint8SliceSlice(outerSize int) gopter.Gen {
   175  	return gen.SliceOfN(
   176  		outerSize,
   177  		gen.SliceOfN(maxNbWords*8, gen.UInt8()),
   178  	)
   179  }
   180  
   181  func uint8SliceSliceToBigIntSlice(f *Extension, in [][]uint8) []big.Int {
   182  	res := make([]big.Int, f.Degree)
   183  	bytes := make([]byte, f.Base.NbWords*8)
   184  
   185  	for i := 0; i < len(res); i++ {
   186  
   187  		j := 0
   188  		for ; j < len(bytes) && j < len(in[i]); j++ {
   189  			bytes[j] = in[i][len(in[i])-j-1]
   190  		}
   191  
   192  		res[i].SetBytes(bytes[:j]).Mod(&res[i], f.Base.ModulusBig)
   193  	}
   194  
   195  	return res
   196  }
   197  
   198  func genField(t *testing.T) gopter.Gen {
   199  	return func(genParams *gopter.GenParameters) *gopter.GenResult {
   200  
   201  		genField := func() *FieldConfig {
   202  
   203  			nbWords := minNbWords + mrand.Intn(maxNbWords-minNbWords) //#nosec G404 -- This is a false positive
   204  			bitLen := nbWords*64 - mrand.Intn(64)                     //#nosec G404 -- This is a false positive
   205  
   206  			if bitLen < 2 {
   207  				bitLen = 2
   208  			}
   209  
   210  			modulus, err := rand.Prime(rand.Reader, bitLen)
   211  			if err != nil {
   212  				t.Error(err)
   213  			}
   214  
   215  			var field *FieldConfig
   216  			field, err = NewFieldConfig("dummy", "DummyElement", "0x"+modulus.Text(16), false)
   217  
   218  			if err == nil {
   219  				if field.NbBits != bitLen || field.NbWords != nbWords {
   220  					err = fmt.Errorf("mismatch: field.NbBits = %d, bitLen = %d, field.NbWords = %d, nbWords = %d", field.NbBits, bitLen, field.NbWords, nbWords)
   221  				}
   222  			}
   223  
   224  			if err != nil {
   225  				t.Error(err)
   226  			}
   227  			return field
   228  		}
   229  
   230  		field := genField()
   231  		genResult := gopter.NewGenResult(field, gopter.NoShrinker)
   232  		return genResult
   233  	}
   234  }
   235  
   236  // bigIntMatchUint64Slice is a test helper to match big.Int words against a uint64 slice
   237  func bigIntMatchUint64Slice(aInt *big.Int, a []uint64) error {
   238  
   239  	words := aInt.Bits()
   240  
   241  	const steps = 64 / bits.UintSize
   242  	const filter uint64 = 0xFFFFFFFFFFFFFFFF >> (64 - bits.UintSize)
   243  	for i := 0; i < len(a)*steps; i++ {
   244  
   245  		var wI big.Word
   246  
   247  		if i < len(words) {
   248  			wI = words[i]
   249  		}
   250  
   251  		aI := a[i/steps] >> ((i * bits.UintSize) % 64)
   252  		aI &= filter
   253  
   254  		if uint64(wI) != aI {
   255  			return fmt.Errorf("bignum mismatch: disagreement on word %d: %x ≠ %x; %d ≠ %d", i, uint64(wI), aI, uint64(wI), aI)
   256  		}
   257  	}
   258  
   259  	return nil
   260  }