github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/orderer/common/cluster/util.go (about) 1 /* 2 Copyright IBM Corp. 2017 All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cluster 8 9 import ( 10 "bytes" 11 "crypto/x509" 12 "encoding/hex" 13 "encoding/json" 14 "encoding/pem" 15 "fmt" 16 "sync" 17 "sync/atomic" 18 "time" 19 20 "github.com/hyperledger/fabric-protos-go/common" 21 "github.com/hyperledger/fabric/bccsp" 22 "github.com/hyperledger/fabric/common/channelconfig" 23 "github.com/hyperledger/fabric/common/configtx" 24 "github.com/hyperledger/fabric/common/flogging" 25 "github.com/hyperledger/fabric/common/policies" 26 "github.com/hyperledger/fabric/common/tools/protolator" 27 "github.com/hyperledger/fabric/common/util" 28 "github.com/hyperledger/fabric/core/comm" 29 "github.com/hyperledger/fabric/protoutil" 30 "github.com/pkg/errors" 31 "google.golang.org/grpc" 32 ) 33 34 // ConnByCertMap maps certificates represented as strings 35 // to gRPC connections 36 type ConnByCertMap map[string]*grpc.ClientConn 37 38 // Lookup looks up a certificate and returns the connection that was mapped 39 // to the certificate, and whether it was found or not 40 func (cbc ConnByCertMap) Lookup(cert []byte) (*grpc.ClientConn, bool) { 41 conn, ok := cbc[string(cert)] 42 return conn, ok 43 } 44 45 // Put associates the given connection to the certificate 46 func (cbc ConnByCertMap) Put(cert []byte, conn *grpc.ClientConn) { 47 cbc[string(cert)] = conn 48 } 49 50 // Remove removes the connection that is associated to the given certificate 51 func (cbc ConnByCertMap) Remove(cert []byte) { 52 delete(cbc, string(cert)) 53 } 54 55 func (cbc ConnByCertMap) Size() int { 56 return len(cbc) 57 } 58 59 // MemberMapping defines NetworkMembers by their ID 60 type MemberMapping map[uint64]*Stub 61 62 // Put inserts the given stub to the MemberMapping 63 func (mp MemberMapping) Put(stub *Stub) { 64 mp[stub.ID] = stub 65 } 66 67 // ByID retrieves the Stub with the given ID from the MemberMapping 68 func (mp MemberMapping) ByID(ID uint64) *Stub { 69 return mp[ID] 70 } 71 72 // LookupByClientCert retrieves a Stub with the given client certificate 73 func (mp MemberMapping) LookupByClientCert(cert []byte) *Stub { 74 for _, stub := range mp { 75 if bytes.Equal(stub.ClientTLSCert, cert) { 76 return stub 77 } 78 } 79 return nil 80 } 81 82 // ServerCertificates returns a set of the server certificates 83 // represented as strings 84 func (mp MemberMapping) ServerCertificates() StringSet { 85 res := make(StringSet) 86 for _, member := range mp { 87 res[string(member.ServerTLSCert)] = struct{}{} 88 } 89 return res 90 } 91 92 // StringSet is a set of strings 93 type StringSet map[string]struct{} 94 95 // union adds the elements of the given set to the StringSet 96 func (ss StringSet) union(set StringSet) { 97 for k := range set { 98 ss[k] = struct{}{} 99 } 100 } 101 102 // subtract removes all elements in the given set from the StringSet 103 func (ss StringSet) subtract(set StringSet) { 104 for k := range set { 105 delete(ss, k) 106 } 107 } 108 109 // PredicateDialer creates gRPC connections 110 // that are only established if the given predicate 111 // is fulfilled 112 type PredicateDialer struct { 113 lock sync.RWMutex 114 Config comm.ClientConfig 115 } 116 117 func (dialer *PredicateDialer) UpdateRootCAs(serverRootCAs [][]byte) { 118 dialer.lock.Lock() 119 defer dialer.lock.Unlock() 120 dialer.Config.SecOpts.ServerRootCAs = serverRootCAs 121 } 122 123 // Dial creates a new gRPC connection that can only be established, if the remote node's 124 // certificate chain satisfy verifyFunc 125 func (dialer *PredicateDialer) Dial(address string, verifyFunc RemoteVerifier) (*grpc.ClientConn, error) { 126 dialer.lock.RLock() 127 cfg := dialer.Config.Clone() 128 dialer.lock.RUnlock() 129 130 cfg.SecOpts.VerifyCertificate = verifyFunc 131 client, err := comm.NewGRPCClient(cfg) 132 if err != nil { 133 return nil, errors.WithStack(err) 134 } 135 return client.NewConnection(address) 136 } 137 138 // DERtoPEM returns a PEM representation of the DER 139 // encoded certificate 140 func DERtoPEM(der []byte) string { 141 return string(pem.EncodeToMemory(&pem.Block{ 142 Type: "CERTIFICATE", 143 Bytes: der, 144 })) 145 } 146 147 // StandardDialer wraps an ClientConfig, and provides 148 // a means to connect according to given EndpointCriteria. 149 type StandardDialer struct { 150 Config comm.ClientConfig 151 } 152 153 // Dial dials an address according to the given EndpointCriteria 154 func (dialer *StandardDialer) Dial(endpointCriteria EndpointCriteria) (*grpc.ClientConn, error) { 155 cfg := dialer.Config.Clone() 156 cfg.SecOpts.ServerRootCAs = endpointCriteria.TLSRootCAs 157 158 client, err := comm.NewGRPCClient(cfg) 159 if err != nil { 160 return nil, errors.Wrap(err, "failed creating gRPC client") 161 } 162 163 return client.NewConnection(endpointCriteria.Endpoint) 164 } 165 166 //go:generate mockery -dir . -name BlockVerifier -case underscore -output ./mocks/ 167 168 // BlockVerifier verifies block signatures. 169 type BlockVerifier interface { 170 // VerifyBlockSignature verifies a signature of a block. 171 // It has an optional argument of a configuration envelope 172 // which would make the block verification to use validation rules 173 // based on the given configuration in the ConfigEnvelope. 174 // If the config envelope passed is nil, then the validation rules used 175 // are the ones that were applied at commit of previous blocks. 176 VerifyBlockSignature(sd []*protoutil.SignedData, config *common.ConfigEnvelope) error 177 } 178 179 // BlockSequenceVerifier verifies that the given consecutive sequence 180 // of blocks is valid. 181 type BlockSequenceVerifier func(blocks []*common.Block, channel string) error 182 183 // Dialer creates a gRPC connection to a remote address 184 type Dialer interface { 185 Dial(endpointCriteria EndpointCriteria) (*grpc.ClientConn, error) 186 } 187 188 // VerifyBlocks verifies the given consecutive sequence of blocks is valid, 189 // and returns nil if it's valid, else an error. 190 func VerifyBlocks(blockBuff []*common.Block, signatureVerifier BlockVerifier) error { 191 if len(blockBuff) == 0 { 192 return errors.New("buffer is empty") 193 } 194 // First, we verify that the block hash in every block is: 195 // Equal to the hash in the header 196 // Equal to the previous hash in the succeeding block 197 for i := range blockBuff { 198 if err := VerifyBlockHash(i, blockBuff); err != nil { 199 return err 200 } 201 } 202 203 var config *common.ConfigEnvelope 204 // Verify all configuration blocks that are found inside the block batch, 205 // with the configuration that was committed (nil) or with one that is picked up 206 // during iteration over the block batch. 207 for _, block := range blockBuff { 208 configFromBlock, err := ConfigFromBlock(block) 209 if err == errNotAConfig { 210 continue 211 } 212 if err != nil { 213 return err 214 } 215 // The block is a configuration block, so verify it 216 if err := VerifyBlockSignature(block, signatureVerifier, config); err != nil { 217 return err 218 } 219 config = configFromBlock 220 } 221 222 // Verify the last block's signature 223 lastBlock := blockBuff[len(blockBuff)-1] 224 return VerifyBlockSignature(lastBlock, signatureVerifier, config) 225 } 226 227 var errNotAConfig = errors.New("not a config block") 228 229 // ConfigFromBlock returns a ConfigEnvelope if exists, or a *NotAConfigBlock error. 230 // It may also return some other error in case parsing failed. 231 func ConfigFromBlock(block *common.Block) (*common.ConfigEnvelope, error) { 232 if block == nil || block.Data == nil || len(block.Data.Data) == 0 { 233 return nil, errors.New("empty block") 234 } 235 txn := block.Data.Data[0] 236 env, err := protoutil.GetEnvelopeFromBlock(txn) 237 if err != nil { 238 return nil, errors.WithStack(err) 239 } 240 payload, err := protoutil.UnmarshalPayload(env.Payload) 241 if err != nil { 242 return nil, errors.WithStack(err) 243 } 244 if block.Header.Number == 0 { 245 configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data) 246 if err != nil { 247 return nil, errors.Wrap(err, "invalid config envelope") 248 } 249 return configEnvelope, nil 250 } 251 if payload.Header == nil { 252 return nil, errors.New("nil header in payload") 253 } 254 chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) 255 if err != nil { 256 return nil, errors.WithStack(err) 257 } 258 if common.HeaderType(chdr.Type) != common.HeaderType_CONFIG { 259 return nil, errNotAConfig 260 } 261 configEnvelope, err := configtx.UnmarshalConfigEnvelope(payload.Data) 262 if err != nil { 263 return nil, errors.Wrap(err, "invalid config envelope") 264 } 265 return configEnvelope, nil 266 } 267 268 // VerifyBlockHash verifies the hash chain of the block with the given index 269 // among the blocks of the given block buffer. 270 func VerifyBlockHash(indexInBuffer int, blockBuff []*common.Block) error { 271 if len(blockBuff) <= indexInBuffer { 272 return errors.Errorf("index %d out of bounds (total %d blocks)", indexInBuffer, len(blockBuff)) 273 } 274 block := blockBuff[indexInBuffer] 275 if block.Header == nil { 276 return errors.New("missing block header") 277 } 278 seq := block.Header.Number 279 dataHash := protoutil.BlockDataHash(block.Data) 280 // Verify data hash matches the hash in the header 281 if !bytes.Equal(dataHash, block.Header.DataHash) { 282 computedHash := hex.EncodeToString(dataHash) 283 claimedHash := hex.EncodeToString(block.Header.DataHash) 284 return errors.Errorf("computed hash of block (%d) (%s) doesn't match claimed hash (%s)", 285 seq, computedHash, claimedHash) 286 } 287 // We have a previous block in the buffer, ensure current block's previous hash matches the previous one. 288 if indexInBuffer > 0 { 289 prevBlock := blockBuff[indexInBuffer-1] 290 currSeq := block.Header.Number 291 if prevBlock.Header == nil { 292 return errors.New("previous block header is nil") 293 } 294 prevSeq := prevBlock.Header.Number 295 if prevSeq+1 != currSeq { 296 return errors.Errorf("sequences %d and %d were received consecutively", prevSeq, currSeq) 297 } 298 if !bytes.Equal(block.Header.PreviousHash, protoutil.BlockHeaderHash(prevBlock.Header)) { 299 claimedPrevHash := hex.EncodeToString(block.Header.PreviousHash) 300 actualPrevHash := hex.EncodeToString(protoutil.BlockHeaderHash(prevBlock.Header)) 301 return errors.Errorf("block [%d]'s hash (%s) mismatches block [%d]'s prev block hash (%s)", 302 prevSeq, actualPrevHash, currSeq, claimedPrevHash) 303 } 304 } 305 return nil 306 } 307 308 // SignatureSetFromBlock creates a signature set out of a block. 309 func SignatureSetFromBlock(block *common.Block) ([]*protoutil.SignedData, error) { 310 if block.Metadata == nil || len(block.Metadata.Metadata) <= int(common.BlockMetadataIndex_SIGNATURES) { 311 return nil, errors.New("no metadata in block") 312 } 313 metadata, err := protoutil.GetMetadataFromBlock(block, common.BlockMetadataIndex_SIGNATURES) 314 if err != nil { 315 return nil, errors.Errorf("failed unmarshaling medatata for signatures: %v", err) 316 } 317 318 var signatureSet []*protoutil.SignedData 319 for _, metadataSignature := range metadata.Signatures { 320 sigHdr, err := protoutil.UnmarshalSignatureHeader(metadataSignature.SignatureHeader) 321 if err != nil { 322 return nil, errors.Errorf("failed unmarshaling signature header for block with id %d: %v", 323 block.Header.Number, err) 324 } 325 signatureSet = append(signatureSet, 326 &protoutil.SignedData{ 327 Identity: sigHdr.Creator, 328 Data: util.ConcatenateBytes(metadata.Value, 329 metadataSignature.SignatureHeader, protoutil.BlockHeaderBytes(block.Header)), 330 Signature: metadataSignature.Signature, 331 }, 332 ) 333 } 334 return signatureSet, nil 335 } 336 337 // VerifyBlockSignature verifies the signature on the block with the given BlockVerifier and the given config. 338 func VerifyBlockSignature(block *common.Block, verifier BlockVerifier, config *common.ConfigEnvelope) error { 339 signatureSet, err := SignatureSetFromBlock(block) 340 if err != nil { 341 return err 342 } 343 return verifier.VerifyBlockSignature(signatureSet, config) 344 } 345 346 // EndpointCriteria defines criteria of how to connect to a remote orderer node. 347 type EndpointCriteria struct { 348 Endpoint string // Endpoint of the form host:port 349 TLSRootCAs [][]byte // PEM encoded TLS root CA certificates 350 } 351 352 // String returns a string representation of this EndpointCriteria 353 func (ep EndpointCriteria) String() string { 354 var formattedCAs []interface{} 355 for _, rawCAFile := range ep.TLSRootCAs { 356 var bl *pem.Block 357 pemContent := rawCAFile 358 for { 359 bl, pemContent = pem.Decode(pemContent) 360 if bl == nil { 361 break 362 } 363 cert, err := x509.ParseCertificate(bl.Bytes) 364 if err != nil { 365 break 366 } 367 368 issuedBy := cert.Issuer.String() 369 if cert.Issuer.String() == cert.Subject.String() { 370 issuedBy = "self" 371 } 372 373 info := make(map[string]interface{}) 374 info["Expired"] = time.Now().After(cert.NotAfter) 375 info["Subject"] = cert.Subject.String() 376 info["Issuer"] = issuedBy 377 formattedCAs = append(formattedCAs, info) 378 } 379 } 380 381 formattedEndpointCriteria := make(map[string]interface{}) 382 formattedEndpointCriteria["Endpoint"] = ep.Endpoint 383 formattedEndpointCriteria["CAs"] = formattedCAs 384 385 rawJSON, err := json.Marshal(formattedEndpointCriteria) 386 if err != nil { 387 return fmt.Sprintf("{\"Endpoint\": \"%s\"}", ep.Endpoint) 388 } 389 390 return string(rawJSON) 391 } 392 393 // EndpointconfigFromConfigBlock retrieves TLS CA certificates and endpoints 394 // from a config block. 395 func EndpointconfigFromConfigBlock(block *common.Block, bccsp bccsp.BCCSP) ([]EndpointCriteria, error) { 396 if block == nil { 397 return nil, errors.New("nil block") 398 } 399 envelopeConfig, err := protoutil.ExtractEnvelope(block, 0) 400 if err != nil { 401 return nil, err 402 } 403 404 bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, bccsp) 405 if err != nil { 406 return nil, errors.Wrap(err, "failed extracting bundle from envelope") 407 } 408 msps, err := bundle.MSPManager().GetMSPs() 409 if err != nil { 410 return nil, errors.Wrap(err, "failed obtaining MSPs from MSPManager") 411 } 412 ordererConfig, ok := bundle.OrdererConfig() 413 if !ok { 414 return nil, errors.New("failed obtaining orderer config from bundle") 415 } 416 417 mspIDsToCACerts := make(map[string][][]byte) 418 var aggregatedTLSCerts [][]byte 419 for _, org := range ordererConfig.Organizations() { 420 // Validate that every orderer org has a corresponding MSP instance in the MSP Manager. 421 msp, exists := msps[org.MSPID()] 422 if !exists { 423 return nil, errors.Errorf("no MSP found for MSP with ID of %s", org.MSPID()) 424 } 425 426 // Build a per org mapping of the TLS CA certs for this org, 427 // and aggregate all TLS CA certs into aggregatedTLSCerts to be used later on. 428 var caCerts [][]byte 429 caCerts = append(caCerts, msp.GetTLSIntermediateCerts()...) 430 caCerts = append(caCerts, msp.GetTLSRootCerts()...) 431 mspIDsToCACerts[org.MSPID()] = caCerts 432 aggregatedTLSCerts = append(aggregatedTLSCerts, caCerts...) 433 } 434 435 endpointsPerOrg := perOrgEndpoints(ordererConfig, mspIDsToCACerts) 436 if len(endpointsPerOrg) > 0 { 437 return endpointsPerOrg, nil 438 } 439 440 return globalEndpointsFromConfig(aggregatedTLSCerts, bundle), nil 441 } 442 443 func perOrgEndpoints(ordererConfig channelconfig.Orderer, mspIDsToCerts map[string][][]byte) []EndpointCriteria { 444 var endpointsPerOrg []EndpointCriteria 445 446 for _, org := range ordererConfig.Organizations() { 447 for _, endpoint := range org.Endpoints() { 448 endpointsPerOrg = append(endpointsPerOrg, EndpointCriteria{ 449 TLSRootCAs: mspIDsToCerts[org.MSPID()], 450 Endpoint: endpoint, 451 }) 452 } 453 } 454 455 return endpointsPerOrg 456 } 457 458 func globalEndpointsFromConfig(aggregatedTLSCerts [][]byte, bundle *channelconfig.Bundle) []EndpointCriteria { 459 var globalEndpoints []EndpointCriteria 460 for _, endpoint := range bundle.ChannelConfig().OrdererAddresses() { 461 globalEndpoints = append(globalEndpoints, EndpointCriteria{ 462 Endpoint: endpoint, 463 TLSRootCAs: aggregatedTLSCerts, 464 }) 465 } 466 return globalEndpoints 467 } 468 469 //go:generate mockery -dir . -name VerifierFactory -case underscore -output ./mocks/ 470 471 // VerifierFactory creates BlockVerifiers. 472 type VerifierFactory interface { 473 // VerifierFromConfig creates a BlockVerifier from the given configuration. 474 VerifierFromConfig(configuration *common.ConfigEnvelope, channel string) (BlockVerifier, error) 475 } 476 477 // VerificationRegistry registers verifiers and retrieves them. 478 type VerificationRegistry struct { 479 LoadVerifier func(chain string) BlockVerifier 480 Logger *flogging.FabricLogger 481 VerifierFactory VerifierFactory 482 VerifiersByChannel map[string]BlockVerifier 483 } 484 485 // RegisterVerifier adds a verifier into the registry if applicable. 486 func (vr *VerificationRegistry) RegisterVerifier(chain string) { 487 if _, exists := vr.VerifiersByChannel[chain]; exists { 488 vr.Logger.Debugf("No need to register verifier for chain %s", chain) 489 return 490 } 491 492 v := vr.LoadVerifier(chain) 493 if v == nil { 494 vr.Logger.Errorf("Failed loading verifier for chain %s", chain) 495 return 496 } 497 498 vr.VerifiersByChannel[chain] = v 499 vr.Logger.Infof("Registered verifier for chain %s", chain) 500 } 501 502 // RetrieveVerifier returns a BlockVerifier for the given channel, or nil if not found. 503 func (vr *VerificationRegistry) RetrieveVerifier(channel string) BlockVerifier { 504 verifier, exists := vr.VerifiersByChannel[channel] 505 if exists { 506 return verifier 507 } 508 vr.Logger.Errorf("No verifier for channel %s exists", channel) 509 return nil 510 } 511 512 // BlockCommitted notifies the VerificationRegistry upon a block commit, which may 513 // trigger a registration of a verifier out of the block in case the block is a config block. 514 func (vr *VerificationRegistry) BlockCommitted(block *common.Block, channel string) { 515 conf, err := ConfigFromBlock(block) 516 // The block doesn't contain a config block, but is a valid block 517 if err == errNotAConfig { 518 vr.Logger.Debugf("Committed block [%d] for channel %s that is not a config block", 519 block.Header.Number, channel) 520 return 521 } 522 // The block isn't a valid block 523 if err != nil { 524 vr.Logger.Errorf("Failed parsing block of channel %s: %v, content: %s", 525 channel, err, BlockToString(block)) 526 return 527 } 528 529 // The block contains a config block 530 verifier, err := vr.VerifierFactory.VerifierFromConfig(conf, channel) 531 if err != nil { 532 vr.Logger.Errorf("Failed creating a verifier from a config block for channel %s: %v, content: %s", 533 channel, err, BlockToString(block)) 534 return 535 } 536 537 vr.VerifiersByChannel[channel] = verifier 538 539 vr.Logger.Debugf("Committed config block [%d] for channel %s", block.Header.Number, channel) 540 } 541 542 // BlockToString returns a string representation of this block. 543 func BlockToString(block *common.Block) string { 544 buff := &bytes.Buffer{} 545 protolator.DeepMarshalJSON(buff, block) 546 return buff.String() 547 } 548 549 // BlockCommitFunc signals a block commit. 550 type BlockCommitFunc func(block *common.Block, channel string) 551 552 // LedgerInterceptor intercepts block commits. 553 type LedgerInterceptor struct { 554 Channel string 555 InterceptBlockCommit BlockCommitFunc 556 LedgerWriter 557 } 558 559 // Append commits a block into the ledger, and also fires the configured callback. 560 func (interceptor *LedgerInterceptor) Append(block *common.Block) error { 561 defer interceptor.InterceptBlockCommit(block, interceptor.Channel) 562 return interceptor.LedgerWriter.Append(block) 563 } 564 565 // BlockVerifierAssembler creates a BlockVerifier out of a config envelope 566 type BlockVerifierAssembler struct { 567 Logger *flogging.FabricLogger 568 BCCSP bccsp.BCCSP 569 } 570 571 // VerifierFromConfig creates a BlockVerifier from the given configuration. 572 func (bva *BlockVerifierAssembler) VerifierFromConfig(configuration *common.ConfigEnvelope, channel string) (BlockVerifier, error) { 573 bundle, err := channelconfig.NewBundle(channel, configuration.Config, bva.BCCSP) 574 if err != nil { 575 return nil, errors.Wrap(err, "failed extracting bundle from envelope") 576 } 577 policyMgr := bundle.PolicyManager() 578 579 return &BlockValidationPolicyVerifier{ 580 Logger: bva.Logger, 581 PolicyMgr: policyMgr, 582 Channel: channel, 583 BCCSP: bva.BCCSP, 584 }, nil 585 } 586 587 // BlockValidationPolicyVerifier verifies signatures based on the block validation policy. 588 type BlockValidationPolicyVerifier struct { 589 Logger *flogging.FabricLogger 590 Channel string 591 PolicyMgr policies.Manager 592 BCCSP bccsp.BCCSP 593 } 594 595 // VerifyBlockSignature verifies the signed data associated to a block, optionally with the given config envelope. 596 func (bv *BlockValidationPolicyVerifier) VerifyBlockSignature(sd []*protoutil.SignedData, envelope *common.ConfigEnvelope) error { 597 policyMgr := bv.PolicyMgr 598 // If the envelope passed isn't nil, we should use a different policy manager. 599 if envelope != nil { 600 bundle, err := channelconfig.NewBundle(bv.Channel, envelope.Config, bv.BCCSP) 601 if err != nil { 602 buff := &bytes.Buffer{} 603 protolator.DeepMarshalJSON(buff, envelope.Config) 604 bv.Logger.Errorf("Failed creating a new bundle for channel %s, Config content is: %s", bv.Channel, buff.String()) 605 return err 606 } 607 bv.Logger.Infof("Initializing new PolicyManager for channel %s", bv.Channel) 608 policyMgr = bundle.PolicyManager() 609 } 610 policy, exists := policyMgr.GetPolicy(policies.BlockValidation) 611 if !exists { 612 return errors.Errorf("policy %s wasn't found", policies.BlockValidation) 613 } 614 return policy.EvaluateSignedData(sd) 615 } 616 617 //go:generate mockery -dir . -name BlockRetriever -case underscore -output ./mocks/ 618 619 // BlockRetriever retrieves blocks 620 type BlockRetriever interface { 621 // Block returns a block with the given number, 622 // or nil if such a block doesn't exist. 623 Block(number uint64) *common.Block 624 } 625 626 // LastConfigBlock returns the last config block relative to the given block. 627 func LastConfigBlock(block *common.Block, blockRetriever BlockRetriever) (*common.Block, error) { 628 if block == nil { 629 return nil, errors.New("nil block") 630 } 631 if blockRetriever == nil { 632 return nil, errors.New("nil blockRetriever") 633 } 634 if block.Metadata == nil || len(block.Metadata.Metadata) <= int(common.BlockMetadataIndex_LAST_CONFIG) { 635 return nil, errors.New("no metadata in block") 636 } 637 lastConfigBlockNum, err := protoutil.GetLastConfigIndexFromBlock(block) 638 if err != nil { 639 return nil, err 640 } 641 lastConfigBlock := blockRetriever.Block(lastConfigBlockNum) 642 if lastConfigBlock == nil { 643 return nil, errors.Errorf("unable to retrieve last config block [%d]", lastConfigBlockNum) 644 } 645 return lastConfigBlock, nil 646 } 647 648 // StreamCountReporter reports the number of streams currently connected to this node 649 type StreamCountReporter struct { 650 Metrics *Metrics 651 count uint32 652 } 653 654 func (scr *StreamCountReporter) Increment() { 655 count := atomic.AddUint32(&scr.count, 1) 656 scr.Metrics.reportStreamCount(count) 657 } 658 659 func (scr *StreamCountReporter) Decrement() { 660 count := atomic.AddUint32(&scr.count, ^uint32(0)) 661 scr.Metrics.reportStreamCount(count) 662 } 663 664 type certificateExpirationCheck struct { 665 minimumExpirationWarningInterval time.Duration 666 expiresAt time.Time 667 expirationWarningThreshold time.Duration 668 lastWarning time.Time 669 nodeName string 670 endpoint string 671 alert func(string, ...interface{}) 672 } 673 674 func (exp *certificateExpirationCheck) checkExpiration(currentTime time.Time, channel string) { 675 timeLeft := exp.expiresAt.Sub(currentTime) 676 if timeLeft > exp.expirationWarningThreshold { 677 return 678 } 679 680 timeSinceLastWarning := currentTime.Sub(exp.lastWarning) 681 if timeSinceLastWarning < exp.minimumExpirationWarningInterval { 682 return 683 } 684 685 exp.alert("Certificate of %s from %s for channel %s expires in less than %v", 686 exp.nodeName, exp.endpoint, channel, timeLeft) 687 exp.lastWarning = currentTime 688 }