github.com/consensys/gnark-crypto@v0.14.0/ecc/bn254/fr/tensor-commitment/commitment_test.go (about) 1 // Copyright 2020 ConsenSys Software Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package tensorcommitment 16 17 import ( 18 "bytes" 19 "hash" 20 "math/big" 21 "math/bits" 22 "strconv" 23 "testing" 24 25 "github.com/consensys/gnark-crypto/ecc/bn254/fr" 26 "github.com/consensys/gnark-crypto/ecc/bn254/fr/sis" 27 "github.com/stretchr/testify/require" 28 ) 29 30 type DummyHash uint 31 32 func (d DummyHash) Write(p []byte) (n int, err error) { 33 return 0, nil 34 } 35 36 func (d DummyHash) Sum(b []byte) []byte { 37 return b 38 } 39 40 func (d DummyHash) Reset() {} 41 42 func (d DummyHash) Size() int { 43 return 0 44 } 45 46 func (d DummyHash) BlockSize() int { 47 return 0 48 } 49 50 func DummyHashMaker() hash.Hash { 51 var res DummyHash 52 return &res 53 } 54 55 func TestAppend(t *testing.T) { 56 if bits.UintSize == 32 { 57 t.Skip("skipping this test in 32bit.") 58 } 59 60 assert := require.New(t) 61 62 // tensor commitment 63 const ( 64 rho = 4 65 nbRows = 10 66 nbColumns = 16 67 ) 68 params, err := NewTCParams(rho, nbColumns, nbRows, DummyHashMaker) 69 assert.NoError(err) 70 71 tc := NewTensorCommitment(params) 72 73 { 74 // random Polynomial of size nbRows 75 p := make([]fr.Element, nbRows) 76 for i := 0; i < nbRows; i++ { 77 p[i].SetRandom() 78 } 79 _, err := tc.Append(p) 80 assert.NoError(err) 81 82 // check if p corresponds to the first column of the state 83 for i := 0; i < nbRows; i++ { 84 assert.True(tc.State[i][0].Equal(&p[i]), "a column is not filled correctly") 85 } 86 87 } 88 89 // after a first polynomial has been filled 90 { 91 // random Polynomial of size nbRows 92 p := make([]fr.Element, nbRows) 93 for i := 0; i < nbRows; i++ { 94 p[i].SetRandom() 95 } 96 _, err := tc.Append(p) 97 assert.NoError(err) 98 99 // check if p corresponds to the second column of the state 100 for i := 0; i < nbRows; i++ { 101 assert.True(tc.State[i][1].Equal(&p[i]), "a column is not filled correctly") 102 } 103 } 104 105 // polynomial whose size is not a multiple of nbRows 106 { 107 // random Polynomial of size nbRows 108 offset := 4 109 p := make([]fr.Element, nbRows+offset) 110 for i := 0; i < nbRows+offset; i++ { 111 p[i].SetRandom() 112 } 113 _, err := tc.Append(p) 114 assert.NoError(err) 115 116 // check if p corresponds to the first column of the state 117 for i := 0; i < nbRows; i++ { 118 assert.True(tc.State[i][2].Equal(&p[i]), "a column is not filled correctly") 119 } 120 for i := 0; i < offset; i++ { 121 assert.True(tc.State[i][3].Equal(&p[i+nbRows]), "a column is not filled correctly") 122 } 123 } 124 125 // same to see if the last column was correctly offset 126 { 127 // random Polynomial of size nbRows 128 offset := 4 129 p := make([]fr.Element, nbRows+offset) 130 for i := 0; i < nbRows+offset; i++ { 131 p[i].SetRandom() 132 } 133 _, err := tc.Append(p) 134 assert.NoError(err) 135 136 // check if p corresponds to the first column of the state 137 for i := 0; i < nbRows; i++ { 138 assert.True(tc.State[i][4].Equal(&p[i]), "a column is not filled correctly") 139 } 140 for i := 0; i < offset; i++ { 141 assert.True(tc.State[i][5].Equal(&p[i+nbRows]), "a column is not filled correctly") 142 } 143 } 144 145 } 146 147 func TestLinearCombination(t *testing.T) { 148 149 rho := 4 150 nbRows := 8 151 nbColumns := 8 152 params, err := NewTCParams(rho, nbColumns, nbRows, DummyHashMaker) 153 if err != nil { 154 t.Fatal(err) 155 } 156 tc := NewTensorCommitment(params) 157 158 // build a random polynomial 159 p := make([]fr.Element, nbRows*nbColumns) 160 for i := 0; i < 64; i++ { 161 p[i].SetRandom() 162 } 163 164 // we select all the entries for the test 165 entryList := make([]int, rho*nbColumns) 166 for i := 0; i < rho*nbColumns; i++ { 167 entryList[i] = i 168 } 169 170 // append p and commit (otherwise the proof cannot be built) 171 tc.Append(p) 172 _, err = tc.Commit() 173 if err != nil { 174 t.Fatal(err) 175 } 176 177 // at each trial, it's the i-th line which is selected 178 for i := 0; i < nbRows; i++ { 179 180 // used for the random linear combination. 181 // it will act as a selector for the test: it selects the i-th 182 // row of p, when p is written as a matrix M_ij, where M_ij=p[i*m+j]. 183 // The i-th entry of l is 1, the others are 0. 184 l := make([]fr.Element, nbRows) 185 l[i].SetInt64(1) 186 187 proof, err := tc.BuildProofAtOnceForTest(l, entryList) 188 if err != nil { 189 t.Fatal(err) 190 } 191 192 // the i-th line of p is the one that is supposed to be selected 193 // (corresponding to the linear combination) 194 expected := make([]fr.Element, nbColumns) 195 for j := 0; j < nbColumns; j++ { 196 expected[j].Set(&p[j*nbRows+i]) 197 } 198 199 for j := 0; j < nbColumns; j++ { 200 if !expected[j].Equal(&proof.LinearCombination[j]) { 201 t.Fatal("expected linear combination is incorrect") 202 } 203 } 204 205 } 206 } 207 208 // Test the verification of a correct proof using a mock hash 209 func TestCommitmentDummyHash(t *testing.T) { 210 211 var rho, nbColumns, nbRows int 212 rho = 4 213 nbColumns = 8 214 nbRows = 8 215 216 var h DummyHash 217 params, err := NewTCParams(rho, nbColumns, nbRows, DummyHashMaker) 218 if err != nil { 219 t.Fatal(err) 220 } 221 tc := NewTensorCommitment(params) 222 223 // random polynomial 224 p := make([]fr.Element, nbRows*nbColumns) 225 for i := 0; i < nbRows*nbColumns; i++ { 226 p[i].SetRandom() 227 } 228 229 // coefficients for the linear combination 230 l := make([]fr.Element, nbRows) 231 for i := 0; i < nbRows; i++ { 232 l[i].SetRandom() 233 } 234 235 // we select all the entries for the test 236 entryList := make([]int, rho*nbColumns) 237 for i := 0; i < rho*nbColumns; i++ { 238 entryList[i] = i 239 } 240 241 // compute the digest... 242 _, err = tc.Append(p) 243 if err != nil { 244 t.Fatal(err) 245 } 246 digest, err := tc.Commit() 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 // build the proof... 252 proof, err := tc.BuildProofAtOnceForTest(l, entryList) 253 if err != nil { 254 t.Fatal(err) 255 } 256 257 // verify that the proof is correct 258 err = Verify(proof, digest, l, h) 259 if err != nil { 260 t.Fatal(err) 261 } 262 263 } 264 265 // Test the opening using a dummy hash 266 func TestOpeningDummyHash(t *testing.T) { 267 268 var rho, nbColumns, nbRows int 269 rho = 4 270 nbColumns = 8 271 nbRows = 8 272 273 params, err := NewTCParams(rho, nbColumns, nbRows, DummyHashMaker) 274 if err != nil { 275 t.Fatal(err) 276 } 277 tc := NewTensorCommitment(params) 278 279 // random polynomial 280 p := make([]fr.Element, nbColumns*nbRows) 281 for i := 0; i < nbColumns*nbRows; i++ { 282 p[i].SetRandom() 283 } 284 285 // the coefficients are (1,x,x^2,..,x^{n-1}) where x is the point 286 // at which the opening is done 287 var xm, x fr.Element 288 x.SetRandom() 289 hi := make([]fr.Element, nbColumns) // stores [1,x^{nbRows},..,x^{nbRows*nbColumns^-1}] 290 lo := make([]fr.Element, nbRows) // stores [1,x,..,x^{nbRows-1}] 291 lo[0].SetInt64(1) 292 hi[0].SetInt64(1) 293 xm.Exp(x, big.NewInt(int64(nbRows))) 294 for i := 1; i < nbColumns; i++ { 295 lo[i].Mul(&lo[i-1], &x) 296 hi[i].Mul(&hi[i-1], &xm) 297 } 298 299 // create the digest before computing the proof 300 _, err = tc.Append(p) 301 if err != nil { 302 t.Fatal(err) 303 } 304 _, err = tc.Commit() 305 if err != nil { 306 t.Fatal(err) 307 } 308 309 // build the proof 310 entryList := make([]int, rho*nbColumns) 311 for i := 0; i < rho*nbColumns; i++ { 312 entryList[i] = i 313 } 314 proof, err := tc.BuildProofAtOnceForTest(lo, entryList) 315 if err != nil { 316 t.Fatal(err) 317 } 318 319 // finish the evaluation by computing 320 // [linearCombination] * [hi]^t 321 var eval, tmp fr.Element 322 for i := 0; i < nbColumns; i++ { 323 tmp.Mul(&proof.LinearCombination[i], &hi[i]) 324 eval.Add(&eval, &tmp) 325 } 326 327 // compute the real evaluation of p at x manually 328 var expectedEval fr.Element 329 for i := 0; i < nbRows*nbColumns; i++ { 330 expectedEval.Mul(&expectedEval, &x) 331 expectedEval.Add(&expectedEval, &p[len(p)-i-1]) 332 } 333 334 // the results coincide 335 if !expectedEval.Equal(&eval) { 336 t.Fatal("p(x) != [ lo ] x M x [ hi ]^t") 337 } 338 339 } 340 341 // Check the commitments are correctly formed when appending a polynomial 342 func TestAppendSis(t *testing.T) { 343 if bits.UintSize == 32 { 344 t.Skip("skipping this test in 32bit.") 345 } 346 const ( 347 rho = 4 348 nbColumns = 8 349 nbRows = 8 350 logTwoDegree = 1 351 logTwoBound = 4 352 ) 353 354 assert := require.New(t) 355 356 // keySize := 256 357 hMaker, err := sis.NewRingSISMaker(5, logTwoDegree, logTwoBound, 8) 358 assert.NoError(err) 359 360 params, err := NewTCParams(rho, nbColumns, nbRows, hMaker) 361 assert.NoError(err) 362 363 tc := NewTensorCommitment(params) 364 365 // random polynomial (that does not fill the full matrix) 366 offset := 4 367 p := make([]fr.Element, nbRows*nbColumns-offset) 368 for i := 0; i < nbRows*nbColumns-offset; i++ { 369 p[i].SetRandom() 370 } 371 372 s, err := tc.Append(p) 373 assert.NoError(err) 374 375 assert.Equal(nbColumns, len(s)) 376 377 // check the hashes of the columns 378 h := hMaker() 379 for i := 0; i < nbColumns-1; i++ { 380 h.Reset() 381 for j := 0; j < nbRows; j++ { 382 h.Write(p[i*nbRows+j].Marshal()) 383 } 384 _s := h.Sum(nil) 385 assert.True(bytes.Equal(_s, s[i]), "error hash column when appending a polynomial for column", i) 386 } 387 388 // last column 389 h.Reset() 390 for i := (nbColumns - 1) * nbRows; i < nbColumns*nbRows-offset; i++ { 391 h.Write(p[i].Marshal()) 392 } 393 var tmp fr.Element 394 for i := nbColumns*nbRows - offset; i < nbColumns*nbRows; i++ { 395 h.Write(tmp.Marshal()) 396 } 397 _s := h.Sum(nil) 398 assert.True(bytes.Equal(_s, s[nbColumns-1]), "error hash column when appending a polynomial") 399 } 400 401 // Test the verification of a correct proof using SIS as hash 402 func TestCommitmentSis(t *testing.T) { 403 if bits.UintSize == 32 { 404 t.Skip("skipping this test in 32bit.") 405 } 406 var rho, nbColumns, nbRows int 407 rho = 4 408 nbColumns = 8 409 nbRows = 8 410 411 logTwoDegree := 1 412 logTwoBound := 4 413 hMaker, err := sis.NewRingSISMaker(5, logTwoDegree, logTwoBound, 8) 414 if err != nil { 415 t.Fatal(err) 416 } 417 418 params, err := NewTCParams(rho, nbColumns, nbRows, hMaker) 419 if err != nil { 420 t.Fatal(err) 421 } 422 tc := NewTensorCommitment(params) 423 424 // random polynomial 425 p := make([]fr.Element, nbRows*nbColumns) 426 for i := 0; i < nbRows*nbColumns; i++ { 427 p[i].SetRandom() 428 } 429 430 // coefficients for the linear combination 431 l := make([]fr.Element, nbRows) 432 for i := 0; i < nbRows; i++ { 433 l[i].SetRandom() 434 } 435 436 // compute the digest... 437 _, err = tc.Append(p) 438 if err != nil { 439 t.Fatal(err) 440 } 441 digest, err := tc.Commit() 442 if err != nil { 443 t.Fatal(err) 444 } 445 446 // test 1: we select all the entries 447 { 448 entryList := make([]int, rho*nbColumns) 449 for i := 0; i < rho*nbColumns; i++ { 450 entryList[i] = i 451 } 452 453 // build the proof... 454 proof, err := tc.BuildProofAtOnceForTest(l, entryList) 455 if err != nil { 456 t.Fatal(err) 457 } 458 459 // verify that the proof is correct 460 err = Verify(proof, digest, l, hMaker()) 461 if err != nil { 462 t.Fatal(err) 463 } 464 } 465 // test 2: we select a subset of the entries 466 { 467 468 entryList := make([]int, 2) 469 entryList[0] = 1 470 entryList[1] = 4 471 472 // build the proof... 473 proof, err := tc.BuildProofAtOnceForTest(l, entryList) 474 if err != nil { 475 t.Fatal(err) 476 } 477 478 // verify that the proof is correct 479 err = Verify(proof, digest, l, hMaker()) 480 if err != nil { 481 t.Fatal(err) 482 } 483 } 484 } 485 486 // benches 487 func BenchmarkTensorCommitment(b *testing.B) { 488 489 // prepare the tensor commitment 490 logTwoDegree := 4 491 logTwoBound := 4 492 rho := 4 493 494 for i := 0; i < 6; i++ { 495 496 nbColumns := (1 << (3 + i)) 497 nbRows := nbColumns 498 499 h, _ := sis.NewRingSISMaker(5, logTwoDegree, logTwoBound, nbRows) 500 params, _ := NewTCParams(rho, nbColumns, nbRows, h) 501 tc := NewTensorCommitment(params) 502 503 // random polynomial 504 p := make([]fr.Element, nbRows*nbColumns) 505 for i := 0; i < nbRows*nbColumns; i++ { 506 p[i].SetRandom() 507 } 508 509 // run the benchmark 510 b.Run("size poly"+strconv.Itoa(nbRows*nbColumns), func(b *testing.B) { 511 b.ResetTimer() 512 for i := 0; i < b.N; i++ { 513 tc.Append(p) 514 tc.Commit() 515 } 516 }) 517 518 } 519 520 }