github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/prepare_test.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package core 18 19 import ( 20 "math/big" 21 "reflect" 22 "testing" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/consensus/istanbul" 26 "github.com/ethereum/go-ethereum/consensus/istanbul/validator" 27 "github.com/ethereum/go-ethereum/crypto" 28 blscrypto "github.com/ethereum/go-ethereum/crypto/bls" 29 ) 30 31 func TestVerifyPreparedCertificate(t *testing.T) { 32 N := uint64(4) // replica 0 is the proposer, it will send messages to others 33 F := uint64(1) 34 sys := NewTestSystemWithBackend(N, F) 35 view := istanbul.View{ 36 Round: big.NewInt(0), 37 Sequence: big.NewInt(1), 38 } 39 proposal := makeBlock(0) 40 41 for _, b := range sys.backends { 42 b.engine.Start() // start Istanbul core 43 } 44 45 testCases := []struct { 46 name string 47 certificate istanbul.PreparedCertificate 48 expectedErr error 49 expectedView *istanbul.View 50 }{ 51 { 52 "Valid PREPARED certificate", 53 sys.getPreparedCertificate(t, []istanbul.View{view}, proposal), 54 nil, 55 &view, 56 }, 57 { 58 "Invalid PREPARED certificate, duplicate message", 59 func() istanbul.PreparedCertificate { 60 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) 61 preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] 62 return preparedCertificate 63 }(), 64 errInvalidPreparedCertificateDuplicate, 65 nil, 66 }, 67 { 68 "Invalid PREPARED certificate, future message", 69 func() istanbul.PreparedCertificate { 70 futureView := istanbul.View{ 71 Round: big.NewInt(0), 72 Sequence: big.NewInt(10), 73 } 74 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{futureView}, proposal) 75 return preparedCertificate 76 }(), 77 errInvalidPreparedCertificateMsgView, 78 nil, 79 }, 80 { 81 "Invalid PREPARED certificate, includes preprepare message", 82 func() istanbul.PreparedCertificate { 83 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) 84 testInvalidMsg, _ := sys.backends[0].getRoundChangeMessage(view, sys.getPreparedCertificate(t, []istanbul.View{view}, proposal)) 85 preparedCertificate.PrepareOrCommitMessages[0] = testInvalidMsg 86 return preparedCertificate 87 }(), 88 errInvalidPreparedCertificateMsgCode, 89 nil, 90 }, 91 { 92 "Invalid PREPARED certificate, hash mismatch", 93 func() istanbul.PreparedCertificate { 94 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view}, proposal) 95 preparedCertificate.PrepareOrCommitMessages[1] = preparedCertificate.PrepareOrCommitMessages[0] 96 preparedCertificate.Proposal = makeBlock(1) 97 return preparedCertificate 98 }(), 99 errInvalidPreparedCertificateDigestMismatch, 100 nil, 101 }, 102 { 103 "Invalid PREPARED certificate, view inconsistencies", 104 func() istanbul.PreparedCertificate { 105 var view2 istanbul.View 106 view2.Sequence = big.NewInt(view.Sequence.Int64()) 107 view2.Round = big.NewInt(view.Round.Int64() + 1) 108 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view, view2}, proposal) 109 return preparedCertificate 110 }(), 111 errInvalidPreparedCertificateInconsistentViews, 112 nil, 113 }, 114 { 115 "Empty certificate", 116 istanbul.EmptyPreparedCertificate(), 117 errInvalidPreparedCertificateNumMsgs, 118 nil, 119 }, 120 } 121 for _, test := range testCases { 122 t.Run(test.name, func(t *testing.T) { 123 for _, backend := range sys.backends { 124 c := backend.engine.(*core) 125 view, err := c.verifyPreparedCertificate(test.certificate) 126 if err != test.expectedErr { 127 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 128 } 129 if err == nil { 130 if view.Cmp(test.expectedView) != 0 { 131 t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) 132 } 133 view, err := c.getViewFromVerifiedPreparedCertificate(test.certificate) 134 if err != nil { 135 t.Errorf("error mismatch: have %v, want nil", err) 136 } 137 if view.Cmp(test.expectedView) != 0 { 138 t.Errorf("view mismatch: have %v, want %v", view, test.expectedView) 139 } 140 } 141 } 142 }) 143 } 144 } 145 146 func TestHandlePrepare(t *testing.T) { 147 N := uint64(4) 148 F := uint64(1) 149 150 proposal := newTestProposal() 151 expectedSubject := &istanbul.Subject{ 152 View: &istanbul.View{ 153 Round: big.NewInt(0), 154 Sequence: proposal.Number(), 155 }, 156 Digest: proposal.Hash(), 157 } 158 159 testCases := []struct { 160 name string 161 system *testSystem 162 expectedErr error 163 }{ 164 { 165 "normal case", 166 func() *testSystem { 167 sys := NewTestSystemWithBackend(N, F) 168 169 for i, backend := range sys.backends { 170 c := backend.engine.(*core) 171 172 c.current = newTestRoundState( 173 &istanbul.View{ 174 Round: big.NewInt(0), 175 Sequence: big.NewInt(1), 176 }, 177 backend.peers, 178 ) 179 180 if i == 0 { 181 // replica 0 is the proposer 182 c.current.(*roundStateImpl).state = StatePreprepared 183 } 184 } 185 return sys 186 }(), 187 nil, 188 }, 189 { 190 "normal case with prepared certificate", 191 func() *testSystem { 192 sys := NewTestSystemWithBackend(N, F) 193 preparedCert := sys.getPreparedCertificate( 194 t, 195 []istanbul.View{ 196 { 197 Round: big.NewInt(0), 198 Sequence: big.NewInt(1), 199 }, 200 }, 201 proposal) 202 203 for i, backend := range sys.backends { 204 c := backend.engine.(*core) 205 c.current = newTestRoundState( 206 &istanbul.View{ 207 Round: big.NewInt(0), 208 Sequence: big.NewInt(1), 209 }, 210 backend.peers, 211 ) 212 c.current.(*roundStateImpl).preparedCertificate = preparedCert 213 214 if i == 0 { 215 // replica 0 is the proposer 216 c.current.(*roundStateImpl).state = StatePreprepared 217 } 218 } 219 return sys 220 }(), 221 nil, 222 }, 223 { 224 "Inconsistent subject due to prepared certificate", 225 func() *testSystem { 226 sys := NewTestSystemWithBackend(N, F) 227 preparedCert := sys.getPreparedCertificate( 228 t, 229 []istanbul.View{ 230 { 231 Round: big.NewInt(0), 232 Sequence: big.NewInt(10), 233 }, 234 }, 235 proposal) 236 237 for i, backend := range sys.backends { 238 c := backend.engine.(*core) 239 c.current = newTestRoundState( 240 &istanbul.View{ 241 Round: big.NewInt(0), 242 Sequence: big.NewInt(1), 243 }, 244 backend.peers, 245 ) 246 c.current.(*roundStateImpl).preparedCertificate = preparedCert 247 248 if i == 0 { 249 // replica 0 is the proposer 250 c.current.(*roundStateImpl).state = StatePreprepared 251 } 252 } 253 return sys 254 }(), 255 errInconsistentSubject, 256 }, 257 { 258 "future message", 259 func() *testSystem { 260 sys := NewTestSystemWithBackend(N, F) 261 262 for i, backend := range sys.backends { 263 c := backend.engine.(*core) 264 if i == 0 { 265 // replica 0 is the proposer 266 c.current = newTestRoundState( 267 expectedSubject.View, 268 backend.peers, 269 ) 270 c.current.(*roundStateImpl).state = StatePreprepared 271 } else { 272 c.current = newTestRoundState( 273 &istanbul.View{ 274 Round: big.NewInt(2), 275 Sequence: big.NewInt(3), 276 }, 277 backend.peers, 278 ) 279 } 280 } 281 return sys 282 }(), 283 errFutureMessage, 284 }, 285 { 286 "subject not match", 287 func() *testSystem { 288 sys := NewTestSystemWithBackend(N, F) 289 290 for i, backend := range sys.backends { 291 c := backend.engine.(*core) 292 if i == 0 { 293 // replica 0 is the proposer 294 c.current = newTestRoundState( 295 expectedSubject.View, 296 backend.peers, 297 ) 298 c.current.(*roundStateImpl).state = StatePreprepared 299 } else { 300 c.current = newTestRoundState( 301 &istanbul.View{ 302 Round: big.NewInt(0), 303 Sequence: big.NewInt(0), 304 }, 305 backend.peers, 306 ) 307 } 308 } 309 return sys 310 }(), 311 errOldMessage, 312 }, 313 { 314 "subject not match", 315 func() *testSystem { 316 sys := NewTestSystemWithBackend(N, F) 317 318 for i, backend := range sys.backends { 319 c := backend.engine.(*core) 320 if i == 0 { 321 // replica 0 is the proposer 322 c.current = newTestRoundState( 323 expectedSubject.View, 324 backend.peers, 325 ) 326 c.current.(*roundStateImpl).state = StatePreprepared 327 } else { 328 c.current = newTestRoundState( 329 &istanbul.View{ 330 Round: big.NewInt(0), 331 Sequence: big.NewInt(1)}, 332 backend.peers, 333 ) 334 } 335 } 336 return sys 337 }(), 338 errInconsistentSubject, 339 }, 340 { 341 "less than 2F+1", 342 func() *testSystem { 343 sys := NewTestSystemWithBackend(N, F) 344 345 // save less than 2*F+1 replica 346 sys.backends = sys.backends[2*int(F)+1:] 347 348 for i, backend := range sys.backends { 349 c := backend.engine.(*core) 350 c.current = newTestRoundState( 351 expectedSubject.View, 352 backend.peers, 353 ) 354 355 if i == 0 { 356 // replica 0 is the proposer 357 c.current.(*roundStateImpl).state = StatePreprepared 358 } 359 } 360 return sys 361 }(), 362 nil, 363 }, 364 // TODO: double send message 365 } 366 367 for _, test := range testCases { 368 t.Run(test.name, func(t *testing.T) { 369 370 test.system.Run(false) 371 372 v0 := test.system.backends[0] 373 r0 := v0.engine.(*core) 374 375 for i, v := range test.system.backends { 376 validator := r0.current.ValidatorSet().GetByIndex(uint64(i)) 377 m, _ := Encode(v.engine.(*core).current.Subject()) 378 err := r0.handlePrepare(&istanbul.Message{ 379 Code: istanbul.MsgPrepare, 380 Msg: m, 381 Address: validator.Address(), 382 }) 383 if err != nil { 384 if err != test.expectedErr { 385 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 386 } 387 return 388 } 389 } 390 391 // prepared is normal case 392 if r0.current.State() != StatePrepared { 393 // There are not enough PREPARE messages in core 394 if r0.current.State() != StatePreprepared { 395 t.Errorf("state mismatch: have %v, want %v", r0.current.State(), StatePreprepared) 396 } 397 if r0.current.Prepares().Size() >= r0.current.ValidatorSet().MinQuorumSize() { 398 t.Errorf("the size of PREPARE messages should be less than %v", 2*r0.current.ValidatorSet().MinQuorumSize()+1) 399 } 400 401 return 402 } 403 404 // core should have MinQuorumSize PREPARE messages 405 if r0.current.Prepares().Size() < r0.current.ValidatorSet().MinQuorumSize() { 406 t.Errorf("the size of PREPARE messages should be greater than or equal to MinQuorumSize: size %v", r0.current.Prepares().Size()) 407 } 408 409 // a message will be delivered to backend if 2F+1 410 if int64(len(v0.sentMsgs)) != 1 { 411 t.Errorf("the Send() should be called once: times %v", len(test.system.backends[0].sentMsgs)) 412 } 413 414 // verify COMMIT messages 415 decodedMsg := new(istanbul.Message) 416 err := decodedMsg.FromPayload(v0.sentMsgs[0], nil) 417 if err != nil { 418 t.Errorf("error mismatch: have %v, want nil", err) 419 } 420 421 if decodedMsg.Code != istanbul.MsgCommit { 422 t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, istanbul.MsgCommit) 423 } 424 var m *istanbul.CommittedSubject 425 err = decodedMsg.Decode(&m) 426 if err != nil { 427 t.Errorf("error mismatch: have %v, want nil", err) 428 } 429 if !reflect.DeepEqual(m.Subject, expectedSubject) { 430 t.Errorf("subject mismatch: have %v, want %v", m, expectedSubject) 431 } 432 }) 433 } 434 } 435 436 // round is not checked for now 437 func TestVerifyPrepare(t *testing.T) { 438 439 // for log purpose 440 privateKey, _ := crypto.GenerateKey() 441 blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) 442 blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) 443 peer := validator.New(getPublicKeyAddress(privateKey), blsPublicKey) 444 valSet := validator.NewSet([]istanbul.ValidatorData{ 445 { 446 peer.Address(), 447 blsPublicKey, 448 }, 449 }) 450 451 sys := NewTestSystemWithBackend(uint64(1), uint64(0)) 452 453 testCases := []struct { 454 expected error 455 456 prepare *istanbul.Subject 457 roundState RoundState 458 }{ 459 { 460 // normal case 461 expected: nil, 462 prepare: &istanbul.Subject{ 463 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 464 Digest: newTestProposal().Hash(), 465 }, 466 roundState: newTestRoundState( 467 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 468 valSet, 469 ), 470 }, 471 { 472 // old message 473 expected: errInconsistentSubject, 474 prepare: &istanbul.Subject{ 475 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 476 Digest: newTestProposal().Hash(), 477 }, 478 roundState: newTestRoundState( 479 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 480 valSet, 481 ), 482 }, 483 { 484 // different digest 485 expected: errInconsistentSubject, 486 prepare: &istanbul.Subject{ 487 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 488 Digest: common.BytesToHash([]byte("1234567890")), 489 }, 490 roundState: newTestRoundState( 491 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 492 valSet, 493 ), 494 }, 495 { 496 // malicious package(lack of sequence) 497 expected: errInconsistentSubject, 498 prepare: &istanbul.Subject{ 499 View: &istanbul.View{Round: big.NewInt(0), Sequence: nil}, 500 Digest: newTestProposal().Hash(), 501 }, 502 roundState: newTestRoundState( 503 &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(1)}, 504 valSet, 505 ), 506 }, 507 { 508 // wrong PREPARE message with same sequence but different round 509 expected: errInconsistentSubject, 510 prepare: &istanbul.Subject{ 511 View: &istanbul.View{Round: big.NewInt(1), Sequence: big.NewInt(0)}, 512 Digest: newTestProposal().Hash(), 513 }, 514 roundState: newTestRoundState( 515 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 516 valSet, 517 ), 518 }, 519 { 520 // wrong PREPARE message with same round but different sequence 521 expected: errInconsistentSubject, 522 prepare: &istanbul.Subject{ 523 View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(1)}, 524 Digest: newTestProposal().Hash(), 525 }, 526 roundState: newTestRoundState( 527 &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(0)}, 528 valSet, 529 ), 530 }, 531 } 532 for i, test := range testCases { 533 c := sys.backends[0].engine.(*core) 534 c.current = test.roundState 535 536 if err := c.verifyPrepare(test.prepare); err != nil { 537 if err != test.expected { 538 t.Errorf("result %d: error mismatch: have %v, want %v", i, err, test.expected) 539 } 540 } 541 } 542 }