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 }