github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/preprepare_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/consensus/istanbul" 25 ) 26 27 func newTestPreprepare(v *istanbul.View) *istanbul.Preprepare { 28 return &istanbul.Preprepare{ 29 View: v, 30 Proposal: newTestProposal(), 31 } 32 } 33 34 func TestHandlePreprepare(t *testing.T) { 35 N := uint64(4) // replica 0 is the proposer, it will send messages to others 36 F := uint64(1) // F does not affect tests 37 38 getRoundState := func(c *core) *roundStateImpl { 39 return c.current.(*rsSaveDecorator).rs.(*roundStateImpl) 40 } 41 42 testCases := []struct { 43 name string 44 system func() *testSystem 45 getCert func(*testSystem) istanbul.RoundChangeCertificate 46 expectedRequest istanbul.Proposal 47 expectedErr error 48 existingBlock bool 49 }{ 50 { 51 "normal case", 52 func() *testSystem { 53 sys := NewTestSystemWithBackend(N, F) 54 55 for _, backend := range sys.backends { 56 backend.engine.(*core).Start() 57 } 58 return sys 59 }, 60 func(_ *testSystem) istanbul.RoundChangeCertificate { 61 return istanbul.RoundChangeCertificate{} 62 }, 63 newTestProposal(), 64 nil, 65 false, 66 }, 67 { 68 "proposal sequence doesn't match message sequence", 69 func() *testSystem { 70 sys := NewTestSystemWithBackend(N, F) 71 72 for _, backend := range sys.backends { 73 backend.engine.(*core).Start() 74 } 75 return sys 76 }, 77 func(_ *testSystem) istanbul.RoundChangeCertificate { 78 return istanbul.RoundChangeCertificate{} 79 }, 80 makeBlock(3), 81 errInvalidProposal, 82 false, 83 }, 84 { 85 "non-proposer", 86 func() *testSystem { 87 sys := NewTestSystemWithBackend(N, F) 88 89 // force remove replica 0, let replica 1 be the proposer 90 sys.backends = sys.backends[1:] 91 92 for i, backend := range sys.backends { 93 backend.engine.(*core).Start() 94 c := backend.engine.(*core) 95 if i != 0 { 96 // replica 0 is the proposer 97 getRoundState(c).state = StatePreprepared 98 } 99 } 100 return sys 101 }, 102 func(_ *testSystem) istanbul.RoundChangeCertificate { 103 return istanbul.RoundChangeCertificate{} 104 }, 105 makeBlock(1), 106 errNotFromProposer, 107 false, 108 }, 109 { 110 "errOldMessage", 111 func() *testSystem { 112 sys := NewTestSystemWithBackend(N, F) 113 114 for i, backend := range sys.backends { 115 backend.engine.(*core).Start() 116 c := backend.engine.(*core) 117 if i != 0 { 118 getRoundState(c).state = StatePreprepared 119 getRoundState(c).sequence = big.NewInt(10) 120 getRoundState(c).round = big.NewInt(10) 121 } 122 } 123 return sys 124 }, 125 func(_ *testSystem) istanbul.RoundChangeCertificate { 126 return istanbul.RoundChangeCertificate{} 127 }, 128 makeBlock(1), 129 errOldMessage, 130 false, 131 }, 132 { 133 "test existing block", 134 func() *testSystem { 135 sys := NewTestSystemWithBackend(N, F) 136 137 for _, backend := range sys.backends { 138 backend.engine.(*core).Start() 139 c := backend.engine.(*core) 140 getRoundState(c).state = StatePreprepared 141 getRoundState(c).sequence = big.NewInt(10) 142 getRoundState(c).round = big.NewInt(10) 143 } 144 return sys 145 }, 146 func(_ *testSystem) istanbul.RoundChangeCertificate { 147 return istanbul.RoundChangeCertificate{} 148 }, 149 // In the method testbackend_test.go:HasBlockMatching(), it will return true if the proposal's block number == 5 150 makeBlock(5), 151 nil, 152 true, 153 }, 154 { 155 "ROUND CHANGE certificate missing", 156 func() *testSystem { 157 sys := NewTestSystemWithBackend(N, F) 158 159 for _, backend := range sys.backends { 160 backend.engine.(*core).Start() 161 c := backend.engine.(*core) 162 getRoundState(c).state = StatePreprepared 163 getRoundState(c).round = big.NewInt(int64(N)) 164 } 165 return sys 166 }, 167 func(_ *testSystem) istanbul.RoundChangeCertificate { 168 return istanbul.RoundChangeCertificate{} 169 }, 170 makeBlock(1), 171 errMissingRoundChangeCertificate, 172 false, 173 }, 174 { 175 "ROUND CHANGE certificate invalid, duplicate messages.", 176 func() *testSystem { 177 sys := NewTestSystemWithBackend(N, F) 178 179 for _, backend := range sys.backends { 180 backend.engine.(*core).Start() 181 c := backend.engine.(*core) 182 getRoundState(c).state = StatePreprepared 183 getRoundState(c).round = big.NewInt(int64(N)) 184 } 185 return sys 186 }, 187 func(sys *testSystem) istanbul.RoundChangeCertificate { 188 // Duplicate messages 189 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate()) 190 roundChangeCertificate.RoundChangeMessages[1] = roundChangeCertificate.RoundChangeMessages[0] 191 return roundChangeCertificate 192 }, 193 makeBlock(1), 194 errInvalidRoundChangeCertificateDuplicate, 195 false, 196 }, 197 { 198 "ROUND CHANGE certificate contains PREPARED certificate with inconsistent views among the cert's messages", 199 func() *testSystem { 200 sys := NewTestSystemWithBackend(N, F) 201 202 for _, backend := range sys.backends { 203 backend.engine.(*core).Start() 204 c := backend.engine.(*core) 205 getRoundState(c).state = StatePreprepared 206 getRoundState(c).round = big.NewInt(int64(N)) 207 getRoundState(c).sequence = big.NewInt(1) 208 c.current.TransitionToPreprepared(&istanbul.Preprepare{ 209 View: &istanbul.View{ 210 Round: big.NewInt(int64(N)), 211 Sequence: big.NewInt(1), 212 }, 213 Proposal: makeBlock(1), 214 RoundChangeCertificate: istanbul.RoundChangeCertificate{}, 215 }) 216 } 217 return sys 218 }, 219 func(sys *testSystem) istanbul.RoundChangeCertificate { 220 view1 := *(sys.backends[0].engine.(*core).current.View()) 221 222 var view2 istanbul.View 223 view2.Sequence = big.NewInt(view1.Sequence.Int64()) 224 view2.Round = big.NewInt(view1.Round.Int64() + 1) 225 226 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{view1, view2}, makeBlock(1)) 227 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) 228 return roundChangeCertificate 229 }, 230 makeBlock(1), 231 errInvalidPreparedCertificateInconsistentViews, 232 false, 233 }, 234 { 235 "ROUND CHANGE certificate contains PREPARED certificate for a different block.", 236 func() *testSystem { 237 sys := NewTestSystemWithBackend(N, F) 238 239 for _, backend := range sys.backends { 240 backend.engine.(*core).Start() 241 c := backend.engine.(*core) 242 getRoundState(c).state = StatePreprepared 243 getRoundState(c).round = big.NewInt(int64(N)) 244 c.current.TransitionToPreprepared(&istanbul.Preprepare{ 245 View: &istanbul.View{ 246 Round: big.NewInt(int64(N)), 247 Sequence: big.NewInt(0), 248 }, 249 Proposal: makeBlock(2), 250 RoundChangeCertificate: istanbul.RoundChangeCertificate{}, 251 }) 252 } 253 return sys 254 }, 255 func(sys *testSystem) istanbul.RoundChangeCertificate { 256 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(2)) 257 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) 258 return roundChangeCertificate 259 }, 260 makeBlock(1), 261 errInvalidPreparedCertificateDigestMismatch, 262 false, 263 }, 264 { 265 "ROUND CHANGE certificate for N+1 round with valid PREPARED certificates", 266 // Round is N+1 to match the correct proposer. 267 func() *testSystem { 268 sys := NewTestSystemWithBackend(N, F) 269 270 for i, backend := range sys.backends { 271 backend.engine.(*core).Start() 272 c := backend.engine.(*core) 273 getRoundState(c).round = big.NewInt(int64(N)) 274 if i != 0 { 275 getRoundState(c).state = StateAcceptRequest 276 } 277 } 278 return sys 279 }, 280 func(sys *testSystem) istanbul.RoundChangeCertificate { 281 preparedCertificate := sys.getPreparedCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, makeBlock(1)) 282 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, preparedCertificate) 283 return roundChangeCertificate 284 }, 285 makeBlock(1), 286 nil, 287 false, 288 }, 289 { 290 "ROUND CHANGE certificate for N+1 round with empty PREPARED certificates", 291 // Round is N+1 to match the correct proposer. 292 func() *testSystem { 293 sys := NewTestSystemWithBackend(N, F) 294 295 for i, backend := range sys.backends { 296 backend.engine.(*core).Start() 297 c := backend.engine.(*core) 298 getRoundState(c).round = big.NewInt(int64(N)) 299 if i != 0 { 300 getRoundState(c).state = StateAcceptRequest 301 } 302 } 303 return sys 304 }, 305 func(sys *testSystem) istanbul.RoundChangeCertificate { 306 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{*(sys.backends[0].engine.(*core).current.View())}, istanbul.EmptyPreparedCertificate()) 307 return roundChangeCertificate 308 }, 309 makeBlock(1), 310 nil, 311 false, 312 }, 313 { 314 "ROUND CHANGE certificate for N+1 or later rounds with empty PREPARED certificates", 315 // Round is N+1 to match the correct proposer. 316 func() *testSystem { 317 sys := NewTestSystemWithBackend(N, F) 318 319 for i, backend := range sys.backends { 320 backend.engine.(*core).Start() 321 c := backend.engine.(*core) 322 getRoundState(c).round = big.NewInt(int64(N)) 323 if i != 0 { 324 getRoundState(c).state = StateAcceptRequest 325 } 326 } 327 return sys 328 }, 329 func(sys *testSystem) istanbul.RoundChangeCertificate { 330 v1 := *(sys.backends[0].engine.(*core).current.View()) 331 v2 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 1)} 332 v3 := istanbul.View{Sequence: v1.Sequence, Round: big.NewInt(v1.Round.Int64() + 2)} 333 roundChangeCertificate := sys.getRoundChangeCertificate(t, []istanbul.View{v1, v2, v3}, istanbul.EmptyPreparedCertificate()) 334 return roundChangeCertificate 335 }, 336 makeBlock(1), 337 nil, 338 false, 339 }, 340 } 341 342 for _, test := range testCases { 343 t.Run(test.name, func(t *testing.T) { 344 345 t.Log("Running", "test", test.name) 346 sys := test.system() 347 closer := sys.Run(false) 348 349 v0 := sys.backends[0] 350 r0 := v0.engine.(*core) 351 352 curView := r0.current.View() 353 354 preprepareView := curView 355 if test.existingBlock { 356 preprepareView = &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)} 357 } 358 359 preprepare := &istanbul.Preprepare{ 360 View: preprepareView, 361 Proposal: test.expectedRequest, 362 RoundChangeCertificate: test.getCert(sys), 363 } 364 365 for i, v := range sys.backends { 366 // i == 0 is primary backend, it is responsible for send PRE-PREPARE messages to others. 367 if i == 0 { 368 continue 369 } 370 371 c := v.engine.(*core) 372 373 m, _ := Encode(preprepare) 374 // run each backends and verify handlePreprepare function. 375 if err := c.handlePreprepare(&istanbul.Message{ 376 Code: istanbul.MsgPreprepare, 377 Msg: m, 378 Address: v0.Address(), 379 }); err != nil { 380 if err != test.expectedErr { 381 t.Errorf("error mismatch: have %v, want %v", err, test.expectedErr) 382 } 383 return 384 } 385 386 if c.current.State() != StatePreprepared { 387 t.Errorf("state mismatch: have %v, want %v", c.current.State(), StatePreprepared) 388 } 389 390 if !test.existingBlock && !reflect.DeepEqual(c.current.Subject().View, curView) { 391 t.Errorf("view mismatch: have %v, want %v", c.current.Subject().View, curView) 392 } 393 394 if test.existingBlock && len(v.sentMsgs) > 0 { 395 t.Errorf("expecting to ignore commits for old messages %v", v.sentMsgs) 396 } else { 397 continue 398 } 399 400 // verify prepare messages 401 decodedMsg := new(istanbul.Message) 402 err := decodedMsg.FromPayload(v.sentMsgs[0], nil) 403 if err != nil { 404 t.Errorf("error mismatch: have %v, want nil", err) 405 } 406 407 expectedCode := istanbul.MsgPrepare 408 if test.existingBlock { 409 expectedCode = istanbul.MsgCommit 410 } 411 if decodedMsg.Code != expectedCode { 412 t.Errorf("message code mismatch: have %v, want %v", decodedMsg.Code, expectedCode) 413 } 414 415 var subject *istanbul.Subject 416 var committedSubject *istanbul.CommittedSubject 417 418 if decodedMsg.Code == istanbul.MsgPrepare { 419 err = decodedMsg.Decode(&subject) 420 } else if decodedMsg.Code == istanbul.MsgCommit { 421 err = decodedMsg.Decode(&committedSubject) 422 subject = committedSubject.Subject 423 } 424 425 if err != nil { 426 t.Errorf("error mismatch: have %v, want nil", err) 427 } 428 429 expectedSubject := c.current.Subject() 430 if test.existingBlock { 431 expectedSubject = &istanbul.Subject{View: &istanbul.View{Round: big.NewInt(0), Sequence: big.NewInt(5)}, 432 Digest: test.expectedRequest.Hash()} 433 } 434 435 if !reflect.DeepEqual(subject, expectedSubject) { 436 t.Errorf("subject mismatch: have %v, want %v", subject, expectedSubject) 437 } 438 439 if expectedCode == istanbul.MsgCommit { 440 srcValidator := c.current.GetValidatorByAddress(v.address) 441 442 if err := c.verifyCommittedSeal(committedSubject, srcValidator); err != nil { 443 t.Errorf("invalid seal. verify commmited seal error: %v, subject: %v, committedSeal: %v", err, expectedSubject, committedSubject.CommittedSeal) 444 } 445 } 446 } 447 448 closer() 449 }) 450 } 451 }