github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/consensus/hotstuff/integration/instance_test.go (about) 1 package integration 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/gammazero/workerpool" 11 "github.com/onflow/crypto" 12 "github.com/rs/zerolog" 13 "github.com/stretchr/testify/mock" 14 "github.com/stretchr/testify/require" 15 "go.uber.org/atomic" 16 17 "github.com/onflow/flow-go/consensus/hotstuff" 18 "github.com/onflow/flow-go/consensus/hotstuff/blockproducer" 19 "github.com/onflow/flow-go/consensus/hotstuff/committees" 20 "github.com/onflow/flow-go/consensus/hotstuff/eventhandler" 21 "github.com/onflow/flow-go/consensus/hotstuff/forks" 22 "github.com/onflow/flow-go/consensus/hotstuff/helper" 23 "github.com/onflow/flow-go/consensus/hotstuff/mocks" 24 "github.com/onflow/flow-go/consensus/hotstuff/model" 25 "github.com/onflow/flow-go/consensus/hotstuff/notifications" 26 "github.com/onflow/flow-go/consensus/hotstuff/notifications/pubsub" 27 "github.com/onflow/flow-go/consensus/hotstuff/pacemaker" 28 "github.com/onflow/flow-go/consensus/hotstuff/pacemaker/timeout" 29 "github.com/onflow/flow-go/consensus/hotstuff/safetyrules" 30 "github.com/onflow/flow-go/consensus/hotstuff/timeoutaggregator" 31 "github.com/onflow/flow-go/consensus/hotstuff/timeoutcollector" 32 "github.com/onflow/flow-go/consensus/hotstuff/validator" 33 "github.com/onflow/flow-go/consensus/hotstuff/voteaggregator" 34 "github.com/onflow/flow-go/consensus/hotstuff/votecollector" 35 "github.com/onflow/flow-go/model/flow" 36 "github.com/onflow/flow-go/module/counters" 37 "github.com/onflow/flow-go/module/irrecoverable" 38 "github.com/onflow/flow-go/module/metrics" 39 module "github.com/onflow/flow-go/module/mock" 40 msig "github.com/onflow/flow-go/module/signature" 41 "github.com/onflow/flow-go/module/util" 42 "github.com/onflow/flow-go/utils/unittest" 43 ) 44 45 type Instance struct { 46 47 // instance parameters 48 participants flow.IdentityList 49 localID flow.Identifier 50 blockVoteIn VoteFilter 51 blockVoteOut VoteFilter 52 blockPropIn ProposalFilter 53 blockPropOut ProposalFilter 54 blockTimeoutObjectIn TimeoutObjectFilter 55 blockTimeoutObjectOut TimeoutObjectFilter 56 stop Condition 57 58 // instance data 59 queue chan interface{} 60 updatingBlocks sync.RWMutex 61 headers map[flow.Identifier]*flow.Header 62 pendings map[flow.Identifier]*model.Proposal // indexed by parent ID 63 64 // mocked dependencies 65 committee *mocks.DynamicCommittee 66 builder *module.Builder 67 finalizer *module.Finalizer 68 persist *mocks.Persister 69 signer *mocks.Signer 70 verifier *mocks.Verifier 71 notifier *MockedCommunicatorConsumer 72 73 // real dependencies 74 pacemaker hotstuff.PaceMaker 75 producer *blockproducer.BlockProducer 76 forks *forks.Forks 77 voteAggregator *voteaggregator.VoteAggregator 78 timeoutAggregator *timeoutaggregator.TimeoutAggregator 79 safetyRules *safetyrules.SafetyRules 80 validator *validator.Validator 81 82 // main logic 83 handler *eventhandler.EventHandler 84 } 85 86 type MockedCommunicatorConsumer struct { 87 notifications.NoopProposalViolationConsumer 88 notifications.NoopParticipantConsumer 89 notifications.NoopFinalizationConsumer 90 *mocks.CommunicatorConsumer 91 } 92 93 func NewMockedCommunicatorConsumer() *MockedCommunicatorConsumer { 94 return &MockedCommunicatorConsumer{ 95 CommunicatorConsumer: &mocks.CommunicatorConsumer{}, 96 } 97 } 98 99 var _ hotstuff.Consumer = (*MockedCommunicatorConsumer)(nil) 100 var _ hotstuff.TimeoutCollectorConsumer = (*Instance)(nil) 101 102 func NewInstance(t *testing.T, options ...Option) *Instance { 103 104 // generate random default identity 105 identity := unittest.IdentityFixture() 106 107 // initialize the default configuration 108 cfg := Config{ 109 Root: DefaultRoot(), 110 Participants: flow.IdentityList{identity}, 111 LocalID: identity.NodeID, 112 Timeouts: timeout.DefaultConfig, 113 IncomingVotes: BlockNoVotes, 114 OutgoingVotes: BlockNoVotes, 115 IncomingProposals: BlockNoProposals, 116 OutgoingProposals: BlockNoProposals, 117 IncomingTimeoutObjects: BlockNoTimeoutObjects, 118 OutgoingTimeoutObjects: BlockNoTimeoutObjects, 119 StopCondition: RightAway, 120 } 121 122 // apply the custom options 123 for _, option := range options { 124 option(&cfg) 125 } 126 127 // check the local ID is a participant 128 var index uint 129 takesPart := false 130 for i, participant := range cfg.Participants { 131 if participant.NodeID == cfg.LocalID { 132 index = uint(i) 133 takesPart = true 134 break 135 } 136 } 137 require.True(t, takesPart) 138 139 // initialize the instance 140 in := Instance{ 141 142 // instance parameters 143 participants: cfg.Participants, 144 localID: cfg.LocalID, 145 blockVoteIn: cfg.IncomingVotes, 146 blockVoteOut: cfg.OutgoingVotes, 147 blockPropIn: cfg.IncomingProposals, 148 blockPropOut: cfg.OutgoingProposals, 149 blockTimeoutObjectIn: cfg.IncomingTimeoutObjects, 150 blockTimeoutObjectOut: cfg.OutgoingTimeoutObjects, 151 stop: cfg.StopCondition, 152 153 // instance data 154 pendings: make(map[flow.Identifier]*model.Proposal), 155 headers: make(map[flow.Identifier]*flow.Header), 156 queue: make(chan interface{}, 1024), 157 158 // instance mocks 159 committee: &mocks.DynamicCommittee{}, 160 builder: &module.Builder{}, 161 persist: &mocks.Persister{}, 162 signer: &mocks.Signer{}, 163 verifier: &mocks.Verifier{}, 164 notifier: NewMockedCommunicatorConsumer(), 165 finalizer: &module.Finalizer{}, 166 } 167 168 // insert root block into headers register 169 in.headers[cfg.Root.ID()] = cfg.Root 170 171 // program the hotstuff committee state 172 in.committee.On("IdentitiesByEpoch", mock.Anything).Return( 173 func(_ uint64) flow.IdentitySkeletonList { 174 return in.participants.ToSkeleton() 175 }, 176 nil, 177 ) 178 for _, participant := range in.participants { 179 in.committee.On("IdentityByBlock", mock.Anything, participant.NodeID).Return(participant, nil) 180 in.committee.On("IdentityByEpoch", mock.Anything, participant.NodeID).Return(&participant.IdentitySkeleton, nil) 181 } 182 in.committee.On("Self").Return(in.localID) 183 in.committee.On("LeaderForView", mock.Anything).Return( 184 func(view uint64) flow.Identifier { 185 return in.participants[int(view)%len(in.participants)].NodeID 186 }, nil, 187 ) 188 in.committee.On("QuorumThresholdForView", mock.Anything).Return(committees.WeightThresholdToBuildQC(in.participants.ToSkeleton().TotalWeight()), nil) 189 in.committee.On("TimeoutThresholdForView", mock.Anything).Return(committees.WeightThresholdToTimeout(in.participants.ToSkeleton().TotalWeight()), nil) 190 191 // program the builder module behaviour 192 in.builder.On("BuildOn", mock.Anything, mock.Anything, mock.Anything).Return( 193 func(parentID flow.Identifier, setter func(*flow.Header) error, sign func(*flow.Header) error) *flow.Header { 194 in.updatingBlocks.Lock() 195 defer in.updatingBlocks.Unlock() 196 197 parent, ok := in.headers[parentID] 198 if !ok { 199 return nil 200 } 201 header := &flow.Header{ 202 ChainID: "chain", 203 ParentID: parentID, 204 ParentView: parent.View, 205 Height: parent.Height + 1, 206 PayloadHash: unittest.IdentifierFixture(), 207 Timestamp: time.Now().UTC(), 208 } 209 require.NoError(t, setter(header)) 210 require.NoError(t, sign(header)) 211 in.headers[header.ID()] = header 212 return header 213 }, 214 func(parentID flow.Identifier, _ func(*flow.Header) error, _ func(*flow.Header) error) error { 215 in.updatingBlocks.RLock() 216 _, ok := in.headers[parentID] 217 in.updatingBlocks.RUnlock() 218 if !ok { 219 return fmt.Errorf("parent block not found (parent: %x)", parentID) 220 } 221 return nil 222 }, 223 ) 224 225 // check on stop condition, stop the tests as soon as entering a certain view 226 in.persist.On("PutSafetyData", mock.Anything).Return(nil) 227 in.persist.On("PutLivenessData", mock.Anything).Return(nil) 228 229 // program the hotstuff signer behaviour 230 in.signer.On("CreateProposal", mock.Anything).Return( 231 func(block *model.Block) *model.Proposal { 232 proposal := &model.Proposal{ 233 Block: block, 234 SigData: nil, 235 } 236 return proposal 237 }, 238 nil, 239 ) 240 in.signer.On("CreateVote", mock.Anything).Return( 241 func(block *model.Block) *model.Vote { 242 vote := &model.Vote{ 243 View: block.View, 244 BlockID: block.BlockID, 245 SignerID: in.localID, 246 SigData: unittest.RandomBytes(msig.SigLen * 2), // double sig, one staking, one beacon 247 } 248 return vote 249 }, 250 nil, 251 ) 252 in.signer.On("CreateTimeout", mock.Anything, mock.Anything, mock.Anything).Return( 253 func(curView uint64, newestQC *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) *model.TimeoutObject { 254 timeoutObject := &model.TimeoutObject{ 255 View: curView, 256 NewestQC: newestQC, 257 LastViewTC: lastViewTC, 258 SignerID: in.localID, 259 SigData: unittest.RandomBytes(msig.SigLen), 260 } 261 return timeoutObject 262 }, 263 nil, 264 ) 265 in.signer.On("CreateQC", mock.Anything).Return( 266 func(votes []*model.Vote) *flow.QuorumCertificate { 267 voterIDs := make(flow.IdentifierList, 0, len(votes)) 268 for _, vote := range votes { 269 voterIDs = append(voterIDs, vote.SignerID) 270 } 271 272 signerIndices, err := msig.EncodeSignersToIndices(in.participants.NodeIDs(), voterIDs) 273 require.NoError(t, err, "could not encode signer indices") 274 275 qc := &flow.QuorumCertificate{ 276 View: votes[0].View, 277 BlockID: votes[0].BlockID, 278 SignerIndices: signerIndices, 279 SigData: nil, 280 } 281 return qc 282 }, 283 nil, 284 ) 285 286 // program the hotstuff verifier behaviour 287 in.verifier.On("VerifyVote", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 288 in.verifier.On("VerifyQC", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 289 in.verifier.On("VerifyTC", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) 290 291 // program the hotstuff communicator behaviour 292 in.notifier.On("OnOwnProposal", mock.Anything, mock.Anything).Run( 293 func(args mock.Arguments) { 294 header, ok := args[0].(*flow.Header) 295 require.True(t, ok) 296 297 // sender should always have the parent 298 in.updatingBlocks.RLock() 299 _, exists := in.headers[header.ParentID] 300 in.updatingBlocks.RUnlock() 301 302 if !exists { 303 t.Fatalf("parent for proposal not found parent: %x", header.ParentID) 304 } 305 306 // convert into proposal immediately 307 proposal := model.ProposalFromFlow(header) 308 309 // store locally and loop back to engine for processing 310 in.ProcessBlock(proposal) 311 }, 312 ) 313 in.notifier.On("OnOwnTimeout", mock.Anything).Run(func(args mock.Arguments) { 314 timeoutObject, ok := args[0].(*model.TimeoutObject) 315 require.True(t, ok) 316 in.queue <- timeoutObject 317 }, 318 ) 319 // in case of single node setup we should just forward vote to our own node 320 // for multi-node setup this method will be overridden 321 in.notifier.On("OnOwnVote", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Run(func(args mock.Arguments) { 322 in.queue <- &model.Vote{ 323 View: args[1].(uint64), 324 BlockID: args[0].(flow.Identifier), 325 SignerID: in.localID, 326 SigData: args[2].([]byte), 327 } 328 }) 329 330 // program the finalizer module behaviour 331 in.finalizer.On("MakeFinal", mock.Anything).Return( 332 func(blockID flow.Identifier) error { 333 334 // as we don't use mocks to assert expectations, but only to 335 // simulate behaviour, we should drop the call data regularly 336 in.updatingBlocks.RLock() 337 block, found := in.headers[blockID] 338 in.updatingBlocks.RUnlock() 339 if !found { 340 return fmt.Errorf("can't broadcast with unknown parent") 341 } 342 if block.Height%100 == 0 { 343 in.committee.Calls = nil 344 in.builder.Calls = nil 345 in.signer.Calls = nil 346 in.verifier.Calls = nil 347 in.notifier.Calls = nil 348 in.finalizer.Calls = nil 349 } 350 351 return nil 352 }, 353 ) 354 355 // initialize error handling and logging 356 var err error 357 zerolog.TimestampFunc = func() time.Time { return time.Now().UTC() } 358 // log with node index an ID 359 log := unittest.Logger().With(). 360 Int("index", int(index)). 361 Hex("node_id", in.localID[:]). 362 Logger() 363 notifier := pubsub.NewDistributor() 364 logConsumer := notifications.NewLogConsumer(log) 365 notifier.AddConsumer(logConsumer) 366 notifier.AddConsumer(in.notifier) 367 368 // initialize the block producer 369 in.producer, err = blockproducer.New(in.signer, in.committee, in.builder) 370 require.NoError(t, err) 371 372 // initialize the finalizer 373 rootBlock := model.BlockFromFlow(cfg.Root) 374 375 signerIndices, err := msig.EncodeSignersToIndices(in.participants.NodeIDs(), in.participants.NodeIDs()) 376 require.NoError(t, err, "could not encode signer indices") 377 378 rootQC := &flow.QuorumCertificate{ 379 View: rootBlock.View, 380 BlockID: rootBlock.BlockID, 381 SignerIndices: signerIndices, 382 } 383 certifiedRootBlock, err := model.NewCertifiedBlock(rootBlock, rootQC) 384 require.NoError(t, err) 385 386 livenessData := &hotstuff.LivenessData{ 387 CurrentView: rootQC.View + 1, 388 NewestQC: rootQC, 389 } 390 391 in.persist.On("GetLivenessData").Return(livenessData, nil).Once() 392 393 // initialize the pacemaker 394 controller := timeout.NewController(cfg.Timeouts) 395 in.pacemaker, err = pacemaker.New(controller, pacemaker.NoProposalDelay(), notifier, in.persist) 396 require.NoError(t, err) 397 398 // initialize the forks handler 399 in.forks, err = forks.New(&certifiedRootBlock, in.finalizer, notifier) 400 require.NoError(t, err) 401 402 // initialize the validator 403 in.validator = validator.New(in.committee, in.verifier) 404 405 weight := uint64(flow.DefaultInitialWeight) 406 407 indices, err := msig.EncodeSignersToIndices(in.participants.NodeIDs(), []flow.Identifier(in.participants.NodeIDs())) 408 require.NoError(t, err) 409 410 packer := &mocks.Packer{} 411 packer.On("Pack", mock.Anything, mock.Anything).Return(indices, unittest.RandomBytes(128), nil).Maybe() 412 413 onQCCreated := func(qc *flow.QuorumCertificate) { 414 in.queue <- qc 415 } 416 417 minRequiredWeight := committees.WeightThresholdToBuildQC(uint64(len(in.participants)) * weight) 418 voteProcessorFactory := mocks.NewVoteProcessorFactory(t) 419 voteProcessorFactory.On("Create", mock.Anything, mock.Anything).Return( 420 func(log zerolog.Logger, proposal *model.Proposal) hotstuff.VerifyingVoteProcessor { 421 stakingSigAggtor := helper.MakeWeightedSignatureAggregator(weight) 422 stakingSigAggtor.On("Verify", mock.Anything, mock.Anything).Return(nil).Maybe() 423 424 rbRector := helper.MakeRandomBeaconReconstructor(msig.RandomBeaconThreshold(len(in.participants))) 425 rbRector.On("Verify", mock.Anything, mock.Anything).Return(nil).Maybe() 426 427 return votecollector.NewCombinedVoteProcessor( 428 log, proposal.Block, 429 stakingSigAggtor, rbRector, 430 onQCCreated, 431 packer, 432 minRequiredWeight, 433 ) 434 }, nil).Maybe() 435 436 voteAggregationDistributor := pubsub.NewVoteAggregationDistributor() 437 createCollectorFactoryMethod := votecollector.NewStateMachineFactory(log, voteAggregationDistributor, voteProcessorFactory.Create) 438 voteCollectors := voteaggregator.NewVoteCollectors(log, livenessData.CurrentView, workerpool.New(2), createCollectorFactoryMethod) 439 440 metricsCollector := metrics.NewNoopCollector() 441 442 // initialize the vote aggregator 443 in.voteAggregator, err = voteaggregator.NewVoteAggregator( 444 log, 445 metricsCollector, 446 metricsCollector, 447 metricsCollector, 448 voteAggregationDistributor, 449 livenessData.CurrentView, 450 voteCollectors, 451 ) 452 require.NoError(t, err) 453 454 // initialize factories for timeout collector and timeout processor 455 timeoutAggregationDistributor := pubsub.NewTimeoutAggregationDistributor() 456 timeoutProcessorFactory := mocks.NewTimeoutProcessorFactory(t) 457 timeoutProcessorFactory.On("Create", mock.Anything).Return( 458 func(view uint64) hotstuff.TimeoutProcessor { 459 // mock signature aggregator which doesn't perform any crypto operations and just tracks total weight 460 aggregator := &mocks.TimeoutSignatureAggregator{} 461 totalWeight := atomic.NewUint64(0) 462 newestView := counters.NewMonotonousCounter(0) 463 aggregator.On("View").Return(view).Maybe() 464 aggregator.On("TotalWeight").Return(func() uint64 { 465 return totalWeight.Load() 466 }).Maybe() 467 aggregator.On("VerifyAndAdd", mock.Anything, mock.Anything, mock.Anything).Return( 468 func(signerID flow.Identifier, _ crypto.Signature, newestQCView uint64) uint64 { 469 newestView.Set(newestQCView) 470 identity, ok := in.participants.ByNodeID(signerID) 471 require.True(t, ok) 472 return totalWeight.Add(identity.InitialWeight) 473 }, nil, 474 ).Maybe() 475 aggregator.On("Aggregate", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return( 476 func() []hotstuff.TimeoutSignerInfo { 477 signersData := make([]hotstuff.TimeoutSignerInfo, 0, len(in.participants)) 478 newestQCView := newestView.Value() 479 for _, signer := range in.participants.NodeIDs() { 480 signersData = append(signersData, hotstuff.TimeoutSignerInfo{ 481 NewestQCView: newestQCView, 482 Signer: signer, 483 }) 484 } 485 return signersData 486 }, 487 unittest.SignatureFixture(), 488 nil, 489 ).Maybe() 490 491 p, err := timeoutcollector.NewTimeoutProcessor( 492 unittest.Logger(), 493 in.committee, 494 in.validator, 495 aggregator, 496 timeoutAggregationDistributor, 497 ) 498 require.NoError(t, err) 499 return p 500 }, nil).Maybe() 501 timeoutCollectorFactory := timeoutcollector.NewTimeoutCollectorFactory( 502 unittest.Logger(), 503 timeoutAggregationDistributor, 504 timeoutProcessorFactory, 505 ) 506 timeoutCollectors := timeoutaggregator.NewTimeoutCollectors( 507 log, 508 metricsCollector, 509 livenessData.CurrentView, 510 timeoutCollectorFactory, 511 ) 512 513 // initialize the timeout aggregator 514 in.timeoutAggregator, err = timeoutaggregator.NewTimeoutAggregator( 515 log, 516 metricsCollector, 517 metricsCollector, 518 metricsCollector, 519 livenessData.CurrentView, 520 timeoutCollectors, 521 ) 522 require.NoError(t, err) 523 524 safetyData := &hotstuff.SafetyData{ 525 LockedOneChainView: rootBlock.View, 526 HighestAcknowledgedView: rootBlock.View, 527 } 528 in.persist.On("GetSafetyData", mock.Anything).Return(safetyData, nil).Once() 529 530 // initialize the safety rules 531 in.safetyRules, err = safetyrules.New(in.signer, in.persist, in.committee) 532 require.NoError(t, err) 533 534 // initialize the event handler 535 in.handler, err = eventhandler.NewEventHandler( 536 log, 537 in.pacemaker, 538 in.producer, 539 in.forks, 540 in.persist, 541 in.committee, 542 in.safetyRules, 543 notifier, 544 ) 545 require.NoError(t, err) 546 547 timeoutAggregationDistributor.AddTimeoutCollectorConsumer(logConsumer) 548 timeoutAggregationDistributor.AddTimeoutCollectorConsumer(&in) 549 550 voteAggregationDistributor.AddVoteCollectorConsumer(logConsumer) 551 552 return &in 553 } 554 555 func (in *Instance) Run() error { 556 ctx, cancel := context.WithCancel(context.Background()) 557 defer func() { 558 cancel() 559 <-util.AllDone(in.voteAggregator, in.timeoutAggregator) 560 }() 561 signalerCtx, _ := irrecoverable.WithSignaler(ctx) 562 in.voteAggregator.Start(signalerCtx) 563 in.timeoutAggregator.Start(signalerCtx) 564 <-util.AllReady(in.voteAggregator, in.timeoutAggregator) 565 566 // start the event handler 567 err := in.handler.Start(ctx) 568 if err != nil { 569 return fmt.Errorf("could not start event handler: %w", err) 570 } 571 572 // run until an error or stop condition is reached 573 for { 574 575 // check on stop conditions 576 if in.stop(in) { 577 return errStopCondition 578 } 579 580 // we handle timeouts with priority 581 select { 582 case <-in.handler.TimeoutChannel(): 583 err := in.handler.OnLocalTimeout() 584 if err != nil { 585 return fmt.Errorf("could not process timeout: %w", err) 586 } 587 default: 588 } 589 590 // check on stop conditions 591 if in.stop(in) { 592 return errStopCondition 593 } 594 595 // otherwise, process first received event 596 select { 597 case <-in.handler.TimeoutChannel(): 598 err := in.handler.OnLocalTimeout() 599 if err != nil { 600 return fmt.Errorf("could not process timeout: %w", err) 601 } 602 case msg := <-in.queue: 603 switch m := msg.(type) { 604 case *model.Proposal: 605 // add block to aggregator 606 in.voteAggregator.AddBlock(m) 607 // then pass to event handler 608 err := in.handler.OnReceiveProposal(m) 609 if err != nil { 610 return fmt.Errorf("could not process proposal: %w", err) 611 } 612 case *model.Vote: 613 in.voteAggregator.AddVote(m) 614 case *model.TimeoutObject: 615 in.timeoutAggregator.AddTimeout(m) 616 case *flow.QuorumCertificate: 617 err := in.handler.OnReceiveQc(m) 618 if err != nil { 619 return fmt.Errorf("could not process received QC: %w", err) 620 } 621 case *flow.TimeoutCertificate: 622 err := in.handler.OnReceiveTc(m) 623 if err != nil { 624 return fmt.Errorf("could not process received TC: %w", err) 625 } 626 case *hotstuff.PartialTcCreated: 627 err := in.handler.OnPartialTcCreated(m) 628 if err != nil { 629 return fmt.Errorf("could not process partial TC: %w", err) 630 } 631 } 632 } 633 } 634 } 635 636 func (in *Instance) ProcessBlock(proposal *model.Proposal) { 637 in.updatingBlocks.Lock() 638 defer in.updatingBlocks.Unlock() 639 _, parentExists := in.headers[proposal.Block.QC.BlockID] 640 641 if parentExists { 642 next := proposal 643 for next != nil { 644 in.headers[next.Block.BlockID] = model.ProposalToFlow(next) 645 646 in.queue <- next 647 // keep processing the pending blocks 648 next = in.pendings[next.Block.QC.BlockID] 649 } 650 } else { 651 // cache it in pendings by ParentID 652 in.pendings[proposal.Block.QC.BlockID] = proposal 653 } 654 } 655 656 func (in *Instance) OnTcConstructedFromTimeouts(tc *flow.TimeoutCertificate) { 657 in.queue <- tc 658 } 659 660 func (in *Instance) OnPartialTcCreated(view uint64, newestQC *flow.QuorumCertificate, lastViewTC *flow.TimeoutCertificate) { 661 in.queue <- &hotstuff.PartialTcCreated{ 662 View: view, 663 NewestQC: newestQC, 664 LastViewTC: lastViewTC, 665 } 666 } 667 668 func (in *Instance) OnNewQcDiscovered(qc *flow.QuorumCertificate) { 669 in.queue <- qc 670 } 671 672 func (in *Instance) OnNewTcDiscovered(tc *flow.TimeoutCertificate) { 673 in.queue <- tc 674 } 675 676 func (in *Instance) OnTimeoutProcessed(*model.TimeoutObject) {}