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