github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/gossip/state/state.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package state 8 9 import ( 10 "bytes" 11 "sync" 12 "sync/atomic" 13 "time" 14 15 pb "github.com/golang/protobuf/proto" 16 vsccErrors "github.com/hechain20/hechain/common/errors" 17 "github.com/hechain20/hechain/gossip/api" 18 "github.com/hechain20/hechain/gossip/comm" 19 common2 "github.com/hechain20/hechain/gossip/common" 20 "github.com/hechain20/hechain/gossip/discovery" 21 "github.com/hechain20/hechain/gossip/metrics" 22 "github.com/hechain20/hechain/gossip/protoext" 23 "github.com/hechain20/hechain/gossip/util" 24 "github.com/hechain20/hechain/protoutil" 25 "github.com/hyperledger/fabric-protos-go/common" 26 proto "github.com/hyperledger/fabric-protos-go/gossip" 27 "github.com/hyperledger/fabric-protos-go/ledger/rwset" 28 "github.com/hyperledger/fabric-protos-go/peer" 29 "github.com/hyperledger/fabric-protos-go/transientstore" 30 "github.com/pkg/errors" 31 ) 32 33 // GossipStateProvider is the interface to acquire sequences of the ledger blocks 34 // capable to full fill missing blocks by running state replication and 35 // sending request to get missing block to other nodes 36 type GossipStateProvider interface { 37 AddPayload(payload *proto.Payload) error 38 39 // Stop terminates state transfer object 40 Stop() 41 } 42 43 const ( 44 stragglerWarningThreshold = 100 45 defAntiEntropyBatchSize = 10 46 defMaxBlockDistance = 20 47 48 blocking = true 49 nonBlocking = false 50 51 enqueueRetryInterval = time.Millisecond * 100 52 ) 53 54 // Configuration keeps state transfer configuration parameters 55 type Configuration struct { 56 AntiEntropyInterval time.Duration 57 AntiEntropyStateResponseTimeout time.Duration 58 AntiEntropyBatchSize uint64 59 MaxBlockDistance int 60 AntiEntropyMaxRetries int 61 ChannelBufferSize int 62 EnableStateTransfer bool 63 } 64 65 // GossipAdapter defines gossip/communication required interface for state provider 66 type GossipAdapter interface { 67 // Send sends a message to remote peers 68 Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer) 69 70 // Accept returns a dedicated read-only channel for messages sent by other nodes that match a certain predicate. 71 // If passThrough is false, the messages are processed by the gossip layer beforehand. 72 // If passThrough is true, the gossip layer doesn't intervene and the messages 73 // can be used to send a reply back to the sender 74 Accept(acceptor common2.MessageAcceptor, passThrough bool) (<-chan *proto.GossipMessage, <-chan protoext.ReceivedMessage) 75 76 // UpdateLedgerHeight updates the ledger height the peer 77 // publishes to other peers in the channel 78 UpdateLedgerHeight(height uint64, channelID common2.ChannelID) 79 80 // PeersOfChannel returns the NetworkMembers considered alive 81 // and also subscribed to the channel given 82 PeersOfChannel(common2.ChannelID) []discovery.NetworkMember 83 } 84 85 // MCSAdapter adapter of message crypto service interface to bound 86 // specific APIs required by state transfer service 87 type MCSAdapter interface { 88 // VerifyBlock returns nil if the block is properly signed, and the claimed seqNum is the 89 // sequence number that the block's header contains. 90 // else returns error 91 VerifyBlock(channelID common2.ChannelID, seqNum uint64, signedBlock *common.Block) error 92 93 // VerifyByChannel checks that signature is a valid signature of message 94 // under a peer's verification key, but also in the context of a specific channel. 95 // If the verification succeeded, Verify returns nil meaning no error occurred. 96 // If peerIdentity is nil, then the verification fails. 97 VerifyByChannel(channelID common2.ChannelID, peerIdentity api.PeerIdentityType, signature, message []byte) error 98 } 99 100 // ledgerResources defines abilities that the ledger provides 101 type ledgerResources interface { 102 // StoreBlock deliver new block with underlined private data 103 // returns missing transaction ids 104 StoreBlock(block *common.Block, data util.PvtDataCollections) error 105 106 // StorePvtData used to persist private data into transient store 107 StorePvtData(txid string, privData *transientstore.TxPvtReadWriteSetWithConfigInfo, blckHeight uint64) error 108 109 // GetPvtDataAndBlockByNum gets block by number and also returns all related private data 110 // that requesting peer is eligible for. 111 // The order of private data in slice of PvtDataCollections doesn't imply the order of 112 // transactions in the block related to these private data, to get the correct placement 113 // need to read TxPvtData.SeqInBlock field 114 GetPvtDataAndBlockByNum(seqNum uint64, peerAuthInfo protoutil.SignedData) (*common.Block, util.PvtDataCollections, error) 115 116 // Get recent block sequence number 117 LedgerHeight() (uint64, error) 118 119 // Close ledgerResources 120 Close() 121 } 122 123 // ServicesMediator aggregated adapter to compound all mediator 124 // required by state transfer into single struct 125 type ServicesMediator struct { 126 GossipAdapter 127 MCSAdapter 128 } 129 130 // GossipStateProviderImpl the implementation of the GossipStateProvider interface 131 // the struct to handle in memory sliding window of 132 // new ledger block to be acquired by hyper ledger 133 type GossipStateProviderImpl struct { 134 logger util.Logger 135 136 // Chain id 137 chainID string 138 139 mediator *ServicesMediator 140 141 // Queue of payloads which wasn't acquired yet 142 payloads PayloadsBuffer 143 144 ledger ledgerResources 145 146 stateResponseCh chan protoext.ReceivedMessage 147 148 stateRequestCh chan protoext.ReceivedMessage 149 150 stopCh chan struct{} 151 152 once sync.Once 153 154 stateTransferActive int32 155 156 stateMetrics *metrics.StateMetrics 157 158 requestValidator *stateRequestValidator 159 160 blockingMode bool 161 162 config *StateConfig 163 } 164 165 // stateRequestValidator facilitates validation of the state request messages 166 type stateRequestValidator struct{} 167 168 // validate checks for RemoteStateRequest message validity 169 func (v *stateRequestValidator) validate(request *proto.RemoteStateRequest, batchSize uint64) error { 170 if request.StartSeqNum > request.EndSeqNum { 171 return errors.Errorf("Invalid sequence interval [%d...%d).", request.StartSeqNum, request.EndSeqNum) 172 } 173 174 if request.EndSeqNum > batchSize+request.StartSeqNum { 175 return errors.Errorf("Requesting blocks range [%d-%d) greater than configured allowed"+ 176 " (%d) batching size for anti-entropy.", request.StartSeqNum, request.EndSeqNum, batchSize) 177 } 178 return nil 179 } 180 181 // NewGossipStateProvider creates state provider with coordinator instance 182 // to orchestrate arrival of private rwsets and blocks before committing them into the ledger. 183 func NewGossipStateProvider( 184 logger util.Logger, 185 chainID string, 186 services *ServicesMediator, 187 ledger ledgerResources, 188 stateMetrics *metrics.StateMetrics, 189 blockingMode bool, 190 config *StateConfig, 191 ) GossipStateProvider { 192 gossipChan, _ := services.Accept(func(message interface{}) bool { 193 // Get only data messages 194 return protoext.IsDataMsg(message.(*proto.GossipMessage)) && 195 bytes.Equal(message.(*proto.GossipMessage).Channel, []byte(chainID)) 196 }, false) 197 198 remoteStateMsgFilter := func(message interface{}) bool { 199 receivedMsg := message.(protoext.ReceivedMessage) 200 msg := receivedMsg.GetGossipMessage() 201 if !(protoext.IsRemoteStateMessage(msg.GossipMessage) || msg.GetPrivateData() != nil) { 202 return false 203 } 204 // Ensure we deal only with messages that belong to this channel 205 if !bytes.Equal(msg.Channel, []byte(chainID)) { 206 return false 207 } 208 connInfo := receivedMsg.GetConnectionInfo() 209 authErr := services.VerifyByChannel(msg.Channel, connInfo.Identity, connInfo.Auth.Signature, connInfo.Auth.SignedData) 210 if authErr != nil { 211 logger.Warning("Got unauthorized request from", string(connInfo.Identity)) 212 return false 213 } 214 return true 215 } 216 217 // Filter message which are only relevant for nodeMetastate transfer 218 _, commChan := services.Accept(remoteStateMsgFilter, true) 219 220 height, err := ledger.LedgerHeight() 221 if height == 0 { 222 // Panic here since this is an indication of invalid situation which should not happen in normal 223 // code path. 224 logger.Panic("Committer height cannot be zero, ledger should include at least one block (genesis).") 225 } 226 227 if err != nil { 228 logger.Error("Could not read ledger info to obtain current ledger height due to: ", errors.WithStack(err)) 229 // Exiting as without ledger it will be impossible 230 // to deliver new blocks 231 return nil 232 } 233 234 s := &GossipStateProviderImpl{ 235 logger: logger, 236 // MessageCryptoService 237 mediator: services, 238 // Chain ID 239 chainID: chainID, 240 // Create a queue for payloads, wrapped in a metrics buffer 241 payloads: &metricsBuffer{ 242 PayloadsBuffer: NewPayloadsBuffer(height), 243 sizeMetrics: stateMetrics.PayloadBufferSize, 244 chainID: chainID, 245 }, 246 ledger: ledger, 247 stateResponseCh: make(chan protoext.ReceivedMessage, config.StateChannelSize), 248 stateRequestCh: make(chan protoext.ReceivedMessage, config.StateChannelSize), 249 stopCh: make(chan struct{}), 250 stateTransferActive: 0, 251 once: sync.Once{}, 252 stateMetrics: stateMetrics, 253 requestValidator: &stateRequestValidator{}, 254 blockingMode: blockingMode, 255 config: config, 256 } 257 258 logger.Infof("Updating metadata information for channel %s, "+ 259 "current ledger sequence is at = %d, next expected block is = %d", chainID, height-1, s.payloads.Next()) 260 logger.Debug("Updating gossip ledger height to", height) 261 services.UpdateLedgerHeight(height, common2.ChannelID(s.chainID)) 262 263 // Listen for incoming communication 264 go s.receiveAndQueueGossipMessages(gossipChan) 265 go s.receiveAndDispatchDirectMessages(commChan) 266 // Deliver in order messages into the incoming channel 267 go s.deliverPayloads() 268 if s.config.StateEnabled { 269 // Execute anti entropy to fill missing gaps 270 go s.antiEntropy() 271 } 272 // Taking care of state request messages 273 go s.processStateRequests() 274 275 return s 276 } 277 278 func (s *GossipStateProviderImpl) receiveAndQueueGossipMessages(ch <-chan *proto.GossipMessage) { 279 for msg := range ch { 280 s.logger.Debug("Received new message via gossip channel") 281 go func(msg *proto.GossipMessage) { 282 if !bytes.Equal(msg.Channel, []byte(s.chainID)) { 283 s.logger.Warning("Received enqueue for channel", 284 string(msg.Channel), "while expecting channel", s.chainID, "ignoring enqueue") 285 return 286 } 287 288 dataMsg := msg.GetDataMsg() 289 if dataMsg != nil { 290 if err := s.addPayload(dataMsg.GetPayload(), nonBlocking); err != nil { 291 s.logger.Warningf("Block [%d] received from gossip wasn't added to payload buffer: %v", dataMsg.Payload.SeqNum, err) 292 return 293 } 294 } else { 295 s.logger.Debug("Gossip message received is not of data message type, usually this should not happen.") 296 } 297 }(msg) 298 } 299 } 300 301 func (s *GossipStateProviderImpl) receiveAndDispatchDirectMessages(ch <-chan protoext.ReceivedMessage) { 302 for msg := range ch { 303 s.logger.Debug("Dispatching a message", msg) 304 go func(msg protoext.ReceivedMessage) { 305 gm := msg.GetGossipMessage() 306 // Check type of the message 307 if protoext.IsRemoteStateMessage(gm.GossipMessage) { 308 s.logger.Debug("Handling direct state transfer message") 309 // Got state transfer request response 310 s.directMessage(msg) 311 } else if gm.GetPrivateData() != nil { 312 s.logger.Debug("Handling private data collection message") 313 // Handling private data replication message 314 s.privateDataMessage(msg) 315 } 316 }(msg) 317 } 318 } 319 320 func (s *GossipStateProviderImpl) privateDataMessage(msg protoext.ReceivedMessage) { 321 if !bytes.Equal(msg.GetGossipMessage().Channel, []byte(s.chainID)) { 322 s.logger.Warning("Received state transfer request for channel", 323 string(msg.GetGossipMessage().Channel), "while expecting channel", s.chainID, "skipping request...") 324 return 325 } 326 327 gossipMsg := msg.GetGossipMessage() 328 pvtDataMsg := gossipMsg.GetPrivateData() 329 330 if pvtDataMsg.Payload == nil { 331 s.logger.Warning("Malformed private data message, no payload provided") 332 return 333 } 334 335 collectionName := pvtDataMsg.Payload.CollectionName 336 txID := pvtDataMsg.Payload.TxId 337 pvtRwSet := pvtDataMsg.Payload.PrivateRwset 338 339 if len(pvtRwSet) == 0 { 340 s.logger.Warning("Malformed private data message, no rwset provided, collection name = ", collectionName) 341 return 342 } 343 344 txPvtRwSet := &rwset.TxPvtReadWriteSet{ 345 DataModel: rwset.TxReadWriteSet_KV, 346 NsPvtRwset: []*rwset.NsPvtReadWriteSet{ 347 { 348 Namespace: pvtDataMsg.Payload.Namespace, 349 CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{{ 350 CollectionName: collectionName, 351 Rwset: pvtRwSet, 352 }}, 353 }, 354 }, 355 } 356 357 txPvtRwSetWithConfig := &transientstore.TxPvtReadWriteSetWithConfigInfo{ 358 PvtRwset: txPvtRwSet, 359 CollectionConfigs: map[string]*peer.CollectionConfigPackage{ 360 pvtDataMsg.Payload.Namespace: pvtDataMsg.Payload.CollectionConfigs, 361 }, 362 } 363 364 if err := s.ledger.StorePvtData(txID, txPvtRwSetWithConfig, pvtDataMsg.Payload.PrivateSimHeight); err != nil { 365 s.logger.Errorf("Wasn't able to persist private data for collection %s, due to %s", collectionName, err) 366 msg.Ack(err) // Sending NACK to indicate failure of storing collection 367 } 368 369 msg.Ack(nil) 370 s.logger.Debug("Private data for collection", collectionName, "has been stored") 371 } 372 373 func (s *GossipStateProviderImpl) directMessage(msg protoext.ReceivedMessage) { 374 s.logger.Debug("[ENTER] -> directMessage") 375 defer s.logger.Debug("[EXIT] -> directMessage") 376 377 if msg == nil { 378 s.logger.Error("Got nil message via end-to-end channel, should not happen!") 379 return 380 } 381 382 if !bytes.Equal(msg.GetGossipMessage().Channel, []byte(s.chainID)) { 383 s.logger.Warning("Received state transfer request for channel", 384 string(msg.GetGossipMessage().Channel), "while expecting channel", s.chainID, "skipping request...") 385 return 386 } 387 388 incoming := msg.GetGossipMessage() 389 390 if incoming.GetStateRequest() != nil { 391 if len(s.stateRequestCh) < s.config.StateChannelSize { 392 // Forward state request to the channel, if there are too 393 // many message of state request ignore to avoid flooding. 394 s.stateRequestCh <- msg 395 } 396 } else if incoming.GetStateResponse() != nil { 397 // If no state transfer procedure activate there is 398 // no reason to process the message 399 if atomic.LoadInt32(&s.stateTransferActive) == 1 { 400 // Send signal of state response message 401 s.stateResponseCh <- msg 402 } 403 } 404 } 405 406 func (s *GossipStateProviderImpl) processStateRequests() { 407 for { 408 msg, stillOpen := <-s.stateRequestCh 409 if !stillOpen { 410 return 411 } 412 s.handleStateRequest(msg) 413 } 414 } 415 416 // handleStateRequest handles state request message, validate batch size, reads current leader state to 417 // obtain required blocks, builds response message and send it back 418 func (s *GossipStateProviderImpl) handleStateRequest(msg protoext.ReceivedMessage) { 419 if msg == nil { 420 return 421 } 422 request := msg.GetGossipMessage().GetStateRequest() 423 424 if err := s.requestValidator.validate(request, s.config.StateBatchSize); err != nil { 425 s.logger.Errorf("State request validation failed, %s. Ignoring request...", err) 426 return 427 } 428 429 currentHeight, err := s.ledger.LedgerHeight() 430 if err != nil { 431 s.logger.Errorf("Cannot access to current ledger height, due to %+v", err) 432 return 433 } 434 if currentHeight < request.EndSeqNum { 435 s.logger.Warningf("Received state request to transfer blocks with sequence numbers higher [%d...%d] "+ 436 "than available in ledger (%d)", request.StartSeqNum, request.StartSeqNum, currentHeight) 437 } 438 439 endSeqNum := min(currentHeight, request.EndSeqNum) 440 441 response := &proto.RemoteStateResponse{Payloads: make([]*proto.Payload, 0)} 442 for seqNum := request.StartSeqNum; seqNum <= endSeqNum; seqNum++ { 443 s.logger.Debug("Reading block ", seqNum, " with private data from the coordinator service") 444 connInfo := msg.GetConnectionInfo() 445 peerAuthInfo := protoutil.SignedData{ 446 Data: connInfo.Auth.SignedData, 447 Signature: connInfo.Auth.Signature, 448 Identity: connInfo.Identity, 449 } 450 block, pvtData, err := s.ledger.GetPvtDataAndBlockByNum(seqNum, peerAuthInfo) 451 if err != nil { 452 s.logger.Errorf("cannot read block number %d from ledger, because %+v, skipping...", seqNum, err) 453 continue 454 } 455 456 if block == nil { 457 s.logger.Errorf("Wasn't able to read block with sequence number %d from ledger, skipping....", seqNum) 458 continue 459 } 460 461 blockBytes, err := pb.Marshal(block) 462 if err != nil { 463 s.logger.Errorf("Could not marshal block: %+v", errors.WithStack(err)) 464 continue 465 } 466 467 var pvtBytes [][]byte 468 if pvtData != nil { 469 // Marshal private data 470 pvtBytes, err = pvtData.Marshal() 471 if err != nil { 472 s.logger.Errorf("Failed to marshal private rwset for block %d due to %+v", seqNum, errors.WithStack(err)) 473 continue 474 } 475 } 476 477 // Appending result to the response 478 response.Payloads = append(response.Payloads, &proto.Payload{ 479 SeqNum: seqNum, 480 Data: blockBytes, 481 PrivateData: pvtBytes, 482 }) 483 } 484 // Sending back response with missing blocks 485 msg.Respond(&proto.GossipMessage{ 486 // Copy nonce field from the request, so it will be possible to match response 487 Nonce: msg.GetGossipMessage().Nonce, 488 Tag: proto.GossipMessage_CHAN_OR_ORG, 489 Channel: []byte(s.chainID), 490 Content: &proto.GossipMessage_StateResponse{StateResponse: response}, 491 }) 492 } 493 494 func (s *GossipStateProviderImpl) handleStateResponse(msg protoext.ReceivedMessage) (uint64, error) { 495 max := uint64(0) 496 // Send signal that response for given nonce has been received 497 response := msg.GetGossipMessage().GetStateResponse() 498 // Extract payloads, verify and push into buffer 499 if len(response.GetPayloads()) == 0 { 500 return uint64(0), errors.New("Received state transfer response without payload") 501 } 502 for _, payload := range response.GetPayloads() { 503 s.logger.Debugf("Received payload with sequence number %d.", payload.SeqNum) 504 block, err := protoutil.UnmarshalBlock(payload.Data) 505 if err != nil { 506 s.logger.Warningf("Error unmarshalling payload to block for sequence number %d, due to %+v", payload.SeqNum, err) 507 return uint64(0), err 508 } 509 510 if err := s.mediator.VerifyBlock(common2.ChannelID(s.chainID), payload.SeqNum, block); err != nil { 511 err = errors.WithStack(err) 512 s.logger.Warningf("Error verifying block with sequence number %d, due to %+v", payload.SeqNum, err) 513 return uint64(0), err 514 } 515 if max < payload.SeqNum { 516 max = payload.SeqNum 517 } 518 519 err = s.addPayload(payload, blocking) 520 if err != nil { 521 s.logger.Warningf("Block [%d] received from block transfer wasn't added to payload buffer: %v", payload.SeqNum, err) 522 } 523 } 524 return max, nil 525 } 526 527 // Stop function sends halting signal to all go routines 528 func (s *GossipStateProviderImpl) Stop() { 529 // Make sure stop won't be executed twice 530 // and stop channel won't be used again 531 s.once.Do(func() { 532 close(s.stopCh) 533 // Close all resources 534 s.ledger.Close() 535 close(s.stateRequestCh) 536 close(s.stateResponseCh) 537 }) 538 } 539 540 func (s *GossipStateProviderImpl) deliverPayloads() { 541 for { 542 select { 543 // Wait for notification that next seq has arrived 544 case <-s.payloads.Ready(): 545 s.logger.Debugf("[%s] Ready to transfer payloads (blocks) to the ledger, next block number is = [%d]", s.chainID, s.payloads.Next()) 546 // Collect all subsequent payloads 547 for payload := s.payloads.Pop(); payload != nil; payload = s.payloads.Pop() { 548 rawBlock := &common.Block{} 549 if err := pb.Unmarshal(payload.Data, rawBlock); err != nil { 550 s.logger.Errorf("Error getting block with seqNum = %d due to (%+v)...dropping block", payload.SeqNum, errors.WithStack(err)) 551 continue 552 } 553 if rawBlock.Data == nil || rawBlock.Header == nil { 554 s.logger.Errorf("Block with claimed sequence %d has no header (%v) or data (%v)", 555 payload.SeqNum, rawBlock.Header, rawBlock.Data) 556 continue 557 } 558 s.logger.Debugf("[%s] Transferring block [%d] with %d transaction(s) to the ledger", s.chainID, payload.SeqNum, len(rawBlock.Data.Data)) 559 560 // Read all private data into slice 561 var p util.PvtDataCollections 562 if payload.PrivateData != nil { 563 err := p.Unmarshal(payload.PrivateData) 564 if err != nil { 565 s.logger.Errorf("Wasn't able to unmarshal private data for block seqNum = %d due to (%+v)...dropping block", payload.SeqNum, errors.WithStack(err)) 566 continue 567 } 568 } 569 if err := s.commitBlock(rawBlock, p); err != nil { 570 if executionErr, isExecutionErr := err.(*vsccErrors.VSCCExecutionFailureError); isExecutionErr { 571 s.logger.Errorf("Failed executing VSCC due to %v. Aborting chain processing", executionErr) 572 return 573 } 574 s.logger.Panicf("Cannot commit block to the ledger due to %+v", errors.WithStack(err)) 575 } 576 } 577 case <-s.stopCh: 578 s.logger.Debug("State provider has been stopped, finishing to push new blocks.") 579 return 580 } 581 } 582 } 583 584 func (s *GossipStateProviderImpl) antiEntropy() { 585 defer s.logger.Debug("State Provider stopped, stopping anti entropy procedure.") 586 587 for { 588 select { 589 case <-s.stopCh: 590 return 591 case <-time.After(s.config.StateCheckInterval): 592 ourHeight, err := s.ledger.LedgerHeight() 593 if err != nil { 594 // Unable to read from ledger continue to the next round 595 s.logger.Errorf("Cannot obtain ledger height, due to %+v", errors.WithStack(err)) 596 continue 597 } 598 if ourHeight == 0 { 599 s.logger.Error("Ledger reported block height of 0 but this should be impossible") 600 continue 601 } 602 maxHeight := s.maxAvailableLedgerHeight() 603 if ourHeight >= maxHeight { 604 continue 605 } 606 607 s.requestBlocksInRange(uint64(ourHeight), uint64(maxHeight)-1) 608 } 609 } 610 } 611 612 // maxAvailableLedgerHeight iterates over all available peers and checks advertised meta state to 613 // find maximum available ledger height across peers 614 func (s *GossipStateProviderImpl) maxAvailableLedgerHeight() uint64 { 615 max := uint64(0) 616 for _, p := range s.mediator.PeersOfChannel(common2.ChannelID(s.chainID)) { 617 if p.Properties == nil { 618 s.logger.Debug("Peer", p.PreferredEndpoint(), "doesn't have properties, skipping it") 619 continue 620 } 621 peerHeight := p.Properties.LedgerHeight 622 if max < peerHeight { 623 max = peerHeight 624 } 625 } 626 return max 627 } 628 629 // requestBlocksInRange capable to acquire blocks with sequence 630 // numbers in the range [start...end). 631 func (s *GossipStateProviderImpl) requestBlocksInRange(start uint64, end uint64) { 632 atomic.StoreInt32(&s.stateTransferActive, 1) 633 defer atomic.StoreInt32(&s.stateTransferActive, 0) 634 635 for prev := start; prev <= end; { 636 next := min(end, prev+s.config.StateBatchSize) 637 638 gossipMsg := s.stateRequestMessage(prev, next) 639 640 responseReceived := false 641 tryCounts := 0 642 643 for !responseReceived { 644 if tryCounts > s.config.StateMaxRetries { 645 s.logger.Warningf("Wasn't able to get blocks in range [%d...%d), after %d retries", 646 prev, next, tryCounts) 647 return 648 } 649 // Select peers to ask for blocks 650 peer, err := s.selectPeerToRequestFrom(next) 651 if err != nil { 652 s.logger.Warningf("Cannot send state request for blocks in range [%d...%d), due to %+v", 653 prev, next, errors.WithStack(err)) 654 return 655 } 656 657 s.logger.Debugf("State transfer, with peer %s, requesting blocks in range [%d...%d), "+ 658 "for chainID %s", peer.Endpoint, prev, next, s.chainID) 659 660 s.mediator.Send(gossipMsg, peer) 661 tryCounts++ 662 663 // Wait until timeout or response arrival 664 select { 665 case msg, stillOpen := <-s.stateResponseCh: 666 if !stillOpen { 667 return 668 } 669 if msg.GetGossipMessage().Nonce != 670 gossipMsg.Nonce { 671 continue 672 } 673 // Got corresponding response for state request, can continue 674 index, err := s.handleStateResponse(msg) 675 if err != nil { 676 s.logger.Warningf("Wasn't able to process state response for "+ 677 "blocks [%d...%d], due to %+v", prev, next, errors.WithStack(err)) 678 continue 679 } 680 prev = index + 1 681 responseReceived = true 682 case <-time.After(s.config.StateResponseTimeout): 683 } 684 } 685 } 686 } 687 688 // stateRequestMessage generates state request message for given blocks in range [beginSeq...endSeq] 689 func (s *GossipStateProviderImpl) stateRequestMessage(beginSeq uint64, endSeq uint64) *proto.GossipMessage { 690 return &proto.GossipMessage{ 691 Nonce: util.RandomUInt64(), 692 Tag: proto.GossipMessage_CHAN_OR_ORG, 693 Channel: []byte(s.chainID), 694 Content: &proto.GossipMessage_StateRequest{ 695 StateRequest: &proto.RemoteStateRequest{ 696 StartSeqNum: beginSeq, 697 EndSeqNum: endSeq, 698 }, 699 }, 700 } 701 } 702 703 // selectPeerToRequestFrom selects peer which has required blocks to ask missing blocks from 704 func (s *GossipStateProviderImpl) selectPeerToRequestFrom(height uint64) (*comm.RemotePeer, error) { 705 // Filter peers which posses required range of missing blocks 706 peers := s.filterPeers(s.hasRequiredHeight(height)) 707 708 n := len(peers) 709 if n == 0 { 710 return nil, errors.New("there are no peers to ask for missing blocks from") 711 } 712 713 // Select peer to ask for blocks 714 return peers[util.RandomInt(n)], nil 715 } 716 717 // filterPeers returns list of peers which aligns the predicate provided 718 func (s *GossipStateProviderImpl) filterPeers(predicate func(peer discovery.NetworkMember) bool) []*comm.RemotePeer { 719 var peers []*comm.RemotePeer 720 721 for _, member := range s.mediator.PeersOfChannel(common2.ChannelID(s.chainID)) { 722 if predicate(member) { 723 peers = append(peers, &comm.RemotePeer{Endpoint: member.PreferredEndpoint(), PKIID: member.PKIid}) 724 } 725 } 726 727 return peers 728 } 729 730 // hasRequiredHeight returns predicate which is capable to filter peers with ledger height above than indicated 731 // by provided input parameter 732 func (s *GossipStateProviderImpl) hasRequiredHeight(height uint64) func(peer discovery.NetworkMember) bool { 733 return func(peer discovery.NetworkMember) bool { 734 if peer.Properties != nil { 735 return peer.Properties.LedgerHeight >= height 736 } 737 s.logger.Debug(peer.PreferredEndpoint(), "doesn't have properties") 738 return false 739 } 740 } 741 742 // AddPayload adds new payload into state. 743 func (s *GossipStateProviderImpl) AddPayload(payload *proto.Payload) error { 744 return s.addPayload(payload, s.blockingMode) 745 } 746 747 // addPayload adds new payload into state. It may (or may not) block according to the 748 // given parameter. If it gets a block while in blocking mode - it would wait until 749 // the block is sent into the payloads buffer. 750 // Else - it may drop the block, if the payload buffer is too full. 751 func (s *GossipStateProviderImpl) addPayload(payload *proto.Payload, blockingMode bool) error { 752 if payload == nil { 753 return errors.New("Given payload is nil") 754 } 755 s.logger.Debugf("[%s] Adding payload to local buffer, blockNum = [%d]", s.chainID, payload.SeqNum) 756 height, err := s.ledger.LedgerHeight() 757 if err != nil { 758 return errors.Wrap(err, "Failed obtaining ledger height") 759 } 760 761 if !blockingMode && payload.SeqNum-height >= uint64(s.config.StateBlockBufferSize) { 762 if s.straggler(height, payload) { 763 s.logger.Warningf("[%s] Current block height (%d) is too far behind other peers at height (%d) to be able to receive blocks "+ 764 "without state transfer which is disabled in the configuration "+ 765 "(peer.gossip.state.enabled = false). Consider enabling it or setting the peer explicitly to be a leader (peer.gossip.orgLeader = true) "+ 766 "in order to pull blocks directly from the ordering service.", 767 s.chainID, height, payload.SeqNum+1) 768 } 769 return errors.Errorf("Ledger height is at %d, cannot enqueue block with sequence of %d", height, payload.SeqNum) 770 } 771 772 for blockingMode && s.payloads.Size() > s.config.StateBlockBufferSize*2 { 773 time.Sleep(enqueueRetryInterval) 774 } 775 776 s.payloads.Push(payload) 777 s.logger.Debugf("Blocks payloads buffer size for channel [%s] is %d blocks", s.chainID, s.payloads.Size()) 778 return nil 779 } 780 781 func (s *GossipStateProviderImpl) straggler(currHeight uint64, receivedPayload *proto.Payload) bool { 782 // If state transfer is disabled, there is no way to request blocks from peers that their ledger has advanced too far. 783 stateDisabled := !s.config.StateEnabled 784 // We are too far behind if we received a block with a sequence number more than stragglerWarningThreshold ahead of our height. 785 tooFarBehind := currHeight+stragglerWarningThreshold < receivedPayload.SeqNum 786 // We depend on other peers for blocks if we use leader election, or we are not explicitly configured to be an org leader. 787 peerDependent := s.config.UseLeaderElection || !s.config.OrgLeader 788 return stateDisabled && tooFarBehind && peerDependent 789 } 790 791 func (s *GossipStateProviderImpl) commitBlock(block *common.Block, pvtData util.PvtDataCollections) error { 792 t1 := time.Now() 793 794 // Commit block with available private transactions 795 if err := s.ledger.StoreBlock(block, pvtData); err != nil { 796 s.logger.Errorf("Got error while committing(%+v)", errors.WithStack(err)) 797 return err 798 } 799 800 sinceT1 := time.Since(t1) 801 s.stateMetrics.CommitDuration.With("channel", s.chainID).Observe(sinceT1.Seconds()) 802 803 // Update ledger height 804 s.mediator.UpdateLedgerHeight(block.Header.Number+1, common2.ChannelID(s.chainID)) 805 s.logger.Debugf("[%s] Committed block [%d] with %d transaction(s)", 806 s.chainID, block.Header.Number, len(block.Data.Data)) 807 808 s.stateMetrics.Height.With("channel", s.chainID).Set(float64(block.Header.Number + 1)) 809 810 return nil 811 } 812 813 func min(a uint64, b uint64) uint64 { 814 return b ^ ((a ^ b) & (-(uint64(a-b) >> 63))) 815 }