github.com/Blockdaemon/celo-blockchain@v0.0.0-20200129231733-e667f6b08419/consensus/istanbul/core/testbackend_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 "crypto/ecdsa" 21 "fmt" 22 "math" 23 "math/big" 24 "testing" 25 "time" 26 27 bls "github.com/celo-org/bls-zexe/go" 28 "github.com/ethereum/go-ethereum/common" 29 "github.com/ethereum/go-ethereum/consensus" 30 "github.com/ethereum/go-ethereum/consensus/istanbul" 31 "github.com/ethereum/go-ethereum/consensus/istanbul/validator" 32 "github.com/ethereum/go-ethereum/core/types" 33 "github.com/ethereum/go-ethereum/crypto" 34 blscrypto "github.com/ethereum/go-ethereum/crypto/bls" 35 "github.com/ethereum/go-ethereum/ethdb" 36 "github.com/ethereum/go-ethereum/event" 37 elog "github.com/ethereum/go-ethereum/log" 38 "github.com/ethereum/go-ethereum/p2p/enode" 39 ) 40 41 var testLogger = elog.New() 42 43 type testSystemBackend struct { 44 id uint64 45 sys *testSystem 46 47 engine Engine 48 peers istanbul.ValidatorSet 49 events *event.TypeMux 50 51 committedMsgs []testCommittedMsgs 52 sentMsgs [][]byte // store the message when Send is called by core 53 54 key ecdsa.PrivateKey 55 blsKey []byte 56 address common.Address 57 db ethdb.Database 58 59 // Function pointer to a verify function, so that the test core_test.go/TestVerifyProposal 60 // can inject in different proposal verification statuses. 61 verifyImpl func(proposal istanbul.Proposal) (time.Duration, error) 62 } 63 64 type testCommittedMsgs struct { 65 commitProposal istanbul.Proposal 66 aggregatedSeal types.IstanbulAggregatedSeal 67 aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal 68 } 69 70 // ============================================== 71 // 72 // define the functions that needs to be provided for Istanbul. 73 74 func (self *testSystemBackend) Authorize(address common.Address, _ istanbul.SignerFn, _ istanbul.BLSSignerFn, _ istanbul.MessageSignerFn) { 75 self.address = address 76 self.engine.SetAddress(address) 77 } 78 79 func (self *testSystemBackend) Address() common.Address { 80 return self.address 81 } 82 83 // Peers returns all connected peers 84 func (self *testSystemBackend) Validators(proposal istanbul.Proposal) istanbul.ValidatorSet { 85 return self.peers 86 } 87 88 func (self *testSystemBackend) NextBlockValidators(proposal istanbul.Proposal) (istanbul.ValidatorSet, error) { 89 //This doesn't really return the next block validators 90 return self.peers, nil 91 } 92 93 func (self *testSystemBackend) EventMux() *event.TypeMux { 94 return self.events 95 } 96 97 func (self *testSystemBackend) Send(message []byte, target common.Address) error { 98 testLogger.Info("enqueuing a message...", "address", self.Address()) 99 self.sentMsgs = append(self.sentMsgs, message) 100 self.sys.queuedMessage <- istanbul.MessageEvent{ 101 Payload: message, 102 } 103 return nil 104 } 105 106 func (self *testSystemBackend) BroadcastConsensusMsg(validators []common.Address, message []byte) error { 107 testLogger.Info("enqueuing a message...", "address", self.Address()) 108 self.sentMsgs = append(self.sentMsgs, message) 109 self.sys.queuedMessage <- istanbul.MessageEvent{ 110 Payload: message, 111 } 112 return nil 113 } 114 115 func (self *testSystemBackend) Gossip(validators []common.Address, message []byte, msgCode uint64, ignoreCache bool) error { 116 return nil 117 } 118 119 func (self *testSystemBackend) SignBlockHeader(data []byte) (blscrypto.SerializedSignature, error) { 120 privateKey, _ := bls.DeserializePrivateKey(self.blsKey) 121 defer privateKey.Destroy() 122 123 signature, _ := privateKey.SignMessage(data, []byte{}, false) 124 defer signature.Destroy() 125 signatureBytes, _ := signature.Serialize() 126 127 return blscrypto.SerializedSignatureFromBytes(signatureBytes) 128 } 129 130 func (self *testSystemBackend) SignBLSWithCompositeHash(data []byte) (blscrypto.SerializedSignature, error) { 131 privateKey, _ := bls.DeserializePrivateKey(self.blsKey) 132 defer privateKey.Destroy() 133 134 signature, _ := privateKey.SignMessage(data, []byte{}, true) 135 defer signature.Destroy() 136 signatureBytes, _ := signature.Serialize() 137 138 return blscrypto.SerializedSignatureFromBytes(signatureBytes) 139 } 140 141 func (self *testSystemBackend) Commit(proposal istanbul.Proposal, aggregatedSeal types.IstanbulAggregatedSeal, aggregatedEpochValidatorSetSeal types.IstanbulEpochValidatorSetSeal) error { 142 testLogger.Info("commit message", "address", self.Address()) 143 self.committedMsgs = append(self.committedMsgs, testCommittedMsgs{ 144 commitProposal: proposal, 145 aggregatedSeal: aggregatedSeal, 146 aggregatedEpochValidatorSetSeal: aggregatedEpochValidatorSetSeal, 147 }) 148 149 // fake new head events 150 go self.events.Post(istanbul.FinalCommittedEvent{}) 151 return nil 152 } 153 154 func (self *testSystemBackend) Verify(proposal istanbul.Proposal) (time.Duration, error) { 155 if self.verifyImpl == nil { 156 return self.verifyWithSuccess(proposal) 157 } else { 158 return self.verifyImpl(proposal) 159 } 160 } 161 162 func (self *testSystemBackend) verifyWithSuccess(proposal istanbul.Proposal) (time.Duration, error) { 163 return 0, nil 164 } 165 166 func (self *testSystemBackend) verifyWithFailure(proposal istanbul.Proposal) (time.Duration, error) { 167 return 0, InvalidProposalError 168 } 169 170 func (self *testSystemBackend) verifyWithFutureProposal(proposal istanbul.Proposal) (time.Duration, error) { 171 return 5, consensus.ErrFutureBlock 172 } 173 174 func (self *testSystemBackend) Sign(data []byte) ([]byte, error) { 175 hashData := crypto.Keccak256(data) 176 return crypto.Sign(hashData, &self.key) 177 } 178 179 func (self *testSystemBackend) CheckSignature([]byte, common.Address, []byte) error { 180 return nil 181 } 182 183 func (self *testSystemBackend) CheckValidatorSignature(data []byte, sig []byte) (common.Address, error) { 184 return istanbul.CheckValidatorSignature(self.peers, data, sig) 185 } 186 187 func (self *testSystemBackend) Hash(b interface{}) common.Hash { 188 return common.BytesToHash([]byte("Test")) 189 } 190 191 func (self *testSystemBackend) NewRequest(request istanbul.Proposal) { 192 go self.events.Post(istanbul.RequestEvent{ 193 Proposal: request, 194 }) 195 } 196 197 func (self *testSystemBackend) GetCurrentHeadBlock() istanbul.Proposal { 198 l := len(self.committedMsgs) 199 if l > 0 { 200 testLogger.Info("have proposal for block", "num", l) 201 return self.committedMsgs[l-1].commitProposal 202 } 203 return makeBlock(0) 204 } 205 206 func (self *testSystemBackend) GetCurrentHeadBlockAndAuthor() (istanbul.Proposal, common.Address) { 207 l := len(self.committedMsgs) 208 if l > 0 { 209 testLogger.Info("have proposal for block", "num", l) 210 return self.committedMsgs[l-1].commitProposal, common.Address{} 211 } 212 return makeBlock(0), common.Address{} 213 } 214 215 func (self *testSystemBackend) LastSubject() (istanbul.Subject, error) { 216 lastProposal := self.GetCurrentHeadBlock() 217 lastView := &istanbul.View{Sequence: lastProposal.Number(), Round: big.NewInt(1)} 218 return istanbul.Subject{View: lastView, Digest: lastProposal.Hash()}, nil 219 } 220 221 // Only block height 5 will return true 222 func (self *testSystemBackend) HasBlock(hash common.Hash, number *big.Int) bool { 223 return number.Cmp(big.NewInt(5)) == 0 224 } 225 226 func (self *testSystemBackend) AuthorForBlock(number uint64) common.Address { 227 return common.Address{} 228 } 229 230 func (self *testSystemBackend) ParentBlockValidators(proposal istanbul.Proposal) istanbul.ValidatorSet { 231 return self.peers 232 } 233 234 func (self *testSystemBackend) finalizeAndReturnMessage(msg *istanbul.Message) (istanbul.Message, error) { 235 message := new(istanbul.Message) 236 data, err := self.engine.(*core).finalizeMessage(msg) 237 if err != nil { 238 return *message, err 239 } 240 err = message.FromPayload(data, self.engine.(*core).validateFn) 241 return *message, err 242 } 243 244 func (self *testSystemBackend) getPrepareMessage(view istanbul.View, digest common.Hash) (istanbul.Message, error) { 245 prepare := &istanbul.Subject{ 246 View: &view, 247 Digest: digest, 248 } 249 250 payload, err := Encode(prepare) 251 if err != nil { 252 return istanbul.Message{}, err 253 } 254 255 msg := &istanbul.Message{ 256 Code: istanbul.MsgPrepare, 257 Msg: payload, 258 } 259 260 return self.finalizeAndReturnMessage(msg) 261 } 262 263 func (self *testSystemBackend) getCommitMessage(view istanbul.View, proposal istanbul.Proposal) (istanbul.Message, error) { 264 subject := &istanbul.Subject{ 265 View: &view, 266 Digest: proposal.Hash(), 267 } 268 269 committedSeal, err := self.engine.(*core).generateCommittedSeal(subject) 270 if err != nil { 271 return istanbul.Message{}, err 272 } 273 274 committedSubject := &istanbul.CommittedSubject{ 275 Subject: subject, 276 CommittedSeal: committedSeal[:], 277 } 278 279 payload, err := Encode(committedSubject) 280 if err != nil { 281 return istanbul.Message{}, err 282 } 283 284 msg := &istanbul.Message{ 285 Code: istanbul.MsgCommit, 286 Msg: payload, 287 } 288 289 // // We swap in the provided proposal so that the message is finalized for the provided proposal 290 // // and not for the current preprepare. 291 // cachePreprepare := self.engine.(*core).current.Preprepare() 292 // fmt.Println("5") 293 // self.engine.(*core).current.TransitionToPreprepared(&istanbul.Preprepare{ 294 // View: &view, 295 // Proposal: proposal, 296 // }) 297 message, err := self.finalizeAndReturnMessage(msg) 298 // self.engine.(*core).current.TransitionToPreprepared(cachePreprepare) 299 return message, err 300 } 301 302 func (self *testSystemBackend) getRoundChangeMessage(view istanbul.View, preparedCert istanbul.PreparedCertificate) (istanbul.Message, error) { 303 rc := &istanbul.RoundChange{ 304 View: &view, 305 PreparedCertificate: preparedCert, 306 } 307 308 payload, err := Encode(rc) 309 if err != nil { 310 return istanbul.Message{}, err 311 } 312 313 msg := &istanbul.Message{ 314 Code: istanbul.MsgRoundChange, 315 Msg: payload, 316 } 317 318 return self.finalizeAndReturnMessage(msg) 319 } 320 321 func (self *testSystemBackend) Enode() *enode.Node { 322 return nil 323 } 324 325 func (self *testSystemBackend) RefreshValPeers(valSet istanbul.ValidatorSet) {} 326 327 func (self *testSystemBackend) setVerifyImpl(verifyImpl func(proposal istanbul.Proposal) (time.Duration, error)) { 328 self.verifyImpl = verifyImpl 329 } 330 331 // ============================================== 332 // 333 // define the struct that need to be provided for integration tests. 334 335 type testSystem struct { 336 backends []*testSystemBackend 337 f uint64 338 n uint64 339 validatorsKeys [][]byte 340 341 queuedMessage chan istanbul.MessageEvent 342 quit chan struct{} 343 } 344 345 func newTestSystem(n uint64, f uint64, keys [][]byte) *testSystem { 346 testLogger.SetHandler(elog.StdoutHandler) 347 return &testSystem{ 348 backends: make([]*testSystemBackend, n), 349 validatorsKeys: keys, 350 f: f, 351 n: n, 352 353 queuedMessage: make(chan istanbul.MessageEvent), 354 quit: make(chan struct{}), 355 } 356 } 357 358 func generateValidators(n int) ([]istanbul.ValidatorData, [][]byte, []*ecdsa.PrivateKey) { 359 vals := make([]istanbul.ValidatorData, 0) 360 blsKeys := make([][]byte, 0) 361 keys := make([]*ecdsa.PrivateKey, 0) 362 for i := 0; i < n; i++ { 363 privateKey, _ := crypto.GenerateKey() 364 blsPrivateKey, _ := blscrypto.ECDSAToBLS(privateKey) 365 blsPublicKey, _ := blscrypto.PrivateToPublic(blsPrivateKey) 366 vals = append(vals, istanbul.ValidatorData{ 367 crypto.PubkeyToAddress(privateKey.PublicKey), 368 blsPublicKey, 369 }) 370 keys = append(keys, privateKey) 371 blsKeys = append(blsKeys, blsPrivateKey) 372 } 373 return vals, blsKeys, keys 374 } 375 376 func newTestValidatorSet(n int) istanbul.ValidatorSet { 377 validators, _, _ := generateValidators(n) 378 return validator.NewSet(validators) 379 } 380 381 // FIXME: int64 is needed for N and F 382 func NewTestSystemWithBackend(n, f uint64) *testSystem { 383 testLogger.SetHandler(elog.StdoutHandler) 384 385 validators, blsKeys, keys := generateValidators(int(n)) 386 sys := newTestSystem(n, f, blsKeys) 387 config := *istanbul.DefaultConfig 388 config.ProposerPolicy = istanbul.RoundRobin 389 config.RoundStateDBPath = "" 390 config.RequestTimeout = 300 391 config.TimeoutBackoffFactor = 100 392 config.MinResendRoundChangeTimeout = 1000 393 config.MaxResendRoundChangeTimeout = 10000 394 395 for i := uint64(0); i < n; i++ { 396 vset := validator.NewSet(validators) 397 backend := sys.NewBackend(i) 398 backend.peers = vset 399 backend.address = vset.GetByIndex(i).Address() 400 backend.key = *keys[i] 401 backend.blsKey = blsKeys[i] 402 403 core := New(backend, &config).(*core) 404 core.logger = testLogger 405 core.validateFn = backend.CheckValidatorSignature 406 407 backend.engine = core 408 } 409 410 return sys 411 } 412 413 // listen will consume messages from queue and deliver a message to core 414 func (t *testSystem) listen() { 415 for { 416 select { 417 case <-t.quit: 418 return 419 case queuedMessage := <-t.queuedMessage: 420 testLogger.Info("consuming a queue message...") 421 for _, backend := range t.backends { 422 go backend.EventMux().Post(queuedMessage) 423 } 424 } 425 } 426 } 427 428 // Run will start system components based on given flag, and returns a closer 429 // function that caller can control lifecycle 430 // 431 // Given a true for core if you want to initialize core engine. 432 func (t *testSystem) Run(core bool) func() { 433 for _, b := range t.backends { 434 if core { 435 err := b.engine.Start() // start Istanbul core 436 if err != nil { 437 fmt.Printf("Error Starting istanbul engine: %s", err) 438 panic("Error Starting istanbul engine") 439 } 440 } 441 } 442 443 go t.listen() 444 closer := func() { t.stop(core) } 445 return closer 446 } 447 448 func (t *testSystem) stop(core bool) { 449 close(t.quit) 450 451 for _, b := range t.backends { 452 if core { 453 err := b.engine.Stop() 454 if err != nil { 455 fmt.Printf("Error Stopping istanbul engine: %s", err) 456 panic("Error Stopping istanbul engine") 457 } 458 } 459 } 460 } 461 462 func (t *testSystem) NewBackend(id uint64) *testSystemBackend { 463 // assume always success 464 ethDB := ethdb.NewMemDatabase() 465 backend := &testSystemBackend{ 466 id: id, 467 sys: t, 468 events: new(event.TypeMux), 469 db: ethDB, 470 } 471 472 t.backends[id] = backend 473 return backend 474 } 475 476 func (t *testSystem) F() uint64 { 477 return t.f 478 } 479 480 func (t *testSystem) MinQuorumSize() uint64 { 481 return uint64(math.Ceil(float64(2*t.n) / 3)) 482 } 483 484 func (sys *testSystem) getPreparedCertificate(t *testing.T, views []istanbul.View, proposal istanbul.Proposal) istanbul.PreparedCertificate { 485 preparedCertificate := istanbul.PreparedCertificate{ 486 Proposal: proposal, 487 PrepareOrCommitMessages: []istanbul.Message{}, 488 } 489 for i, backend := range sys.backends { 490 if uint64(i) == sys.MinQuorumSize() { 491 break 492 } 493 var err error 494 var msg istanbul.Message 495 if i%2 == 0 { 496 msg, err = backend.getPrepareMessage(views[i%len(views)], proposal.Hash()) 497 } else { 498 msg, err = backend.getCommitMessage(views[i%len(views)], proposal) 499 } 500 if err != nil { 501 t.Errorf("Failed to create message %v: %v", i, err) 502 } 503 preparedCertificate.PrepareOrCommitMessages = append(preparedCertificate.PrepareOrCommitMessages, msg) 504 } 505 return preparedCertificate 506 } 507 508 func (sys *testSystem) getRoundChangeCertificate(t *testing.T, views []istanbul.View, preparedCertificate istanbul.PreparedCertificate) istanbul.RoundChangeCertificate { 509 var roundChangeCertificate istanbul.RoundChangeCertificate 510 for i, backend := range sys.backends { 511 if uint64(i) == sys.MinQuorumSize() { 512 break 513 } 514 msg, err := backend.getRoundChangeMessage(views[i%len(views)], preparedCertificate) 515 if err != nil { 516 t.Errorf("Failed to create ROUND CHANGE message: %v", err) 517 } 518 roundChangeCertificate.RoundChangeMessages = append(roundChangeCertificate.RoundChangeMessages, msg) 519 } 520 return roundChangeCertificate 521 } 522 523 // ============================================== 524 // 525 // helper functions. 526 527 func getPublicKeyAddress(privateKey *ecdsa.PrivateKey) common.Address { 528 return crypto.PubkeyToAddress(privateKey.PublicKey) 529 }