go_study/fabric-main/orderer/common/follower/block_puller.go

184 lines
7.4 KiB
Go

/*
Copyright IBM Corp. 2017 All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package follower
import (
"encoding/pem"
"github.com/hyperledger/fabric-protos-go/common"
"github.com/hyperledger/fabric/bccsp"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/internal/pkg/identity"
"github.com/hyperledger/fabric/orderer/common/cluster"
"github.com/hyperledger/fabric/orderer/common/localconfig"
"github.com/hyperledger/fabric/protoutil"
"github.com/pkg/errors"
)
//go:generate counterfeiter -o mocks/channel_puller.go -fake-name ChannelPuller . ChannelPuller
// ChannelPuller pulls blocks for a channel
type ChannelPuller interface {
PullBlock(seq uint64) *common.Block
HeightsByEndpoints() (map[string]uint64, error)
UpdateEndpoints(endpoints []cluster.EndpointCriteria)
Close()
}
// BlockPullerCreator creates a ChannelPuller on demand.
// It also maintains a link to a block signature verifier, and exposes a method to update it on incoming config blocks.
// The ChannelPuller generated by this factory always accesses the updated verifier, since it is generated
// with a link to the factory's VerifyBlockSequence method.
type BlockPullerCreator struct {
JoinBlock *common.Block
channelID string
bccsp bccsp.BCCSP
blockSigVerifierFactory cluster.VerifierFactory // Creates a new block signature verifier
blockSigVerifier protoutil.BlockVerifierFunc // The current block signature verifier, from the latest channel config
clusterConfig localconfig.Cluster
signer identity.SignerSerializer
der *pem.Block
stdDialer *cluster.StandardDialer
ClusterVerifyBlocks ClusterVerifyBlocksFunc // Default: cluster.VerifyBlocks, or a mock for testing
vb protoutil.VerifierBuilder
}
// ClusterVerifyBlocksFunc is a function that matches the signature of cluster.VerifyBlocks, and allows mocks for testing.
type ClusterVerifyBlocksFunc func(blockBuff []*common.Block, signatureVerifier protoutil.BlockVerifierFunc, vb protoutil.VerifierBuilder) error
// NewBlockPullerCreator creates a new BlockPullerCreator, using the configuration details that do not change during
// the life cycle of the orderer.
func NewBlockPullerCreator(
channelID string,
logger *flogging.FabricLogger,
signer identity.SignerSerializer,
baseDialer *cluster.PredicateDialer,
clusterConfig localconfig.Cluster,
bccsp bccsp.BCCSP,
) (*BlockPullerCreator, error) {
stdDialer := &cluster.StandardDialer{
Config: baseDialer.Config,
}
stdDialer.Config.AsyncConnect = false
stdDialer.Config.SecOpts.VerifyCertificate = nil
der, _ := pem.Decode(stdDialer.Config.SecOpts.Certificate)
if der == nil {
return nil, errors.Errorf("client certificate isn't in PEM format: %v",
string(stdDialer.Config.SecOpts.Certificate))
}
factory := &BlockPullerCreator{
channelID: channelID,
bccsp: bccsp,
blockSigVerifierFactory: &cluster.BlockVerifierAssembler{
Logger: logger,
BCCSP: bccsp,
},
clusterConfig: clusterConfig,
signer: signer,
stdDialer: stdDialer,
der: der,
ClusterVerifyBlocks: cluster.VerifyBlocksBFT, // The default block sequence verification method.
vb: cluster.BlockVerifierBuilder(bccsp),
}
return factory, nil
}
// BlockPuller creates a block puller on demand, taking the endpoints from the config block.
func (creator *BlockPullerCreator) BlockPuller(configBlock *common.Block, stopChannel chan struct{}) (ChannelPuller, error) {
// Extract the TLS CA certs and endpoints from the join-block
endpoints, err := cluster.EndpointconfigFromConfigBlock(configBlock, creator.bccsp)
if err != nil {
return nil, errors.WithMessage(err, "error extracting endpoints from config block")
}
creator.JoinBlock = configBlock
bp := &cluster.BlockPuller{
VerifyBlockSequence: creator.VerifyBlockSequence,
Logger: flogging.MustGetLogger("orderer.common.cluster.puller").With("channel", creator.channelID),
RetryTimeout: creator.clusterConfig.ReplicationRetryTimeout,
MaxTotalBufferBytes: creator.clusterConfig.ReplicationBufferSize,
MaxPullBlockRetries: uint64(creator.clusterConfig.ReplicationMaxRetries),
FetchTimeout: creator.clusterConfig.ReplicationPullTimeout,
Endpoints: endpoints,
Signer: creator.signer,
TLSCert: creator.der.Bytes,
Channel: creator.channelID,
Dialer: creator.stdDialer,
StopChannel: stopChannel,
}
return bp, nil
}
// UpdateVerifierFromConfigBlock creates a new block signature verifier from the config block and updates the internal
// link to said verifier.
func (creator *BlockPullerCreator) UpdateVerifierFromConfigBlock(configBlock *common.Block) error {
configEnv, err := cluster.ConfigFromBlock(configBlock)
if err != nil {
return errors.WithMessage(err, "failed to extract config envelope from block")
}
verifier, err := creator.blockSigVerifierFactory.VerifierFromConfig(configEnv, creator.channelID)
if err != nil {
return errors.WithMessage(err, "failed to construct a block signature verifier from config envelope")
}
creator.blockSigVerifier = verifier
return nil
}
// VerifyBlockSequence verifies a sequence of blocks, using the internal block signature verifier. It also bootstraps
// the block sig verifier form the genesis block if it does not exist, and skips verifying the genesis block.
func (creator *BlockPullerCreator) VerifyBlockSequence(blocks []*common.Block, _ string) error {
if len(blocks) == 0 {
return errors.New("buffer is empty")
}
if blocks[0] == nil {
return errors.New("first block is nil")
}
if blocks[0].Header == nil {
return errors.New("first block header is nil")
}
if blocks[0].Header.Number == 0 {
if creator.JoinBlock != nil && creator.JoinBlock.Header.Number == 0 {
// If we have joined with a genesis block,
// replace the genesis block we got from the network
// with our own.
blocks[0] = creator.JoinBlock
}
configEnv, err := cluster.ConfigFromBlock(blocks[0])
if err != nil {
return errors.WithMessage(err, "failed to extract config envelope from genesis block")
}
// Bootstrap the verifier from the genesis block, as it will be used to verify
// the subsequent blocks in the batch.
creator.blockSigVerifier, err = creator.blockSigVerifierFactory.VerifierFromConfig(configEnv, creator.channelID)
if err != nil {
return errors.WithMessage(err, "failed to construct a block signature verifier from genesis block")
}
blocksAfterGenesis := blocks[1:]
if len(blocksAfterGenesis) == 0 {
return nil
}
// TODO: we should revisit this as in theory a malicious node can give an incorrect genesis block.
// However, if that happens, then the onboarding node would deviate from the correct nodes and it will be detected by us
// due to follower chain comparing the block it is joined with with the block it pulled by doing:
// if c.joinBlock != nil && !proto.Equal(c.ledgerResources.Block(c.joinBlock.Header.Number).Data, c.joinBlock.Data) {
// c.logger.Panicf("Join block (%d) we pulled mismatches block we joined with", c.joinBlock.Header.Number)
// }
return creator.ClusterVerifyBlocks(blocksAfterGenesis, creator.blockSigVerifier, creator.vb)
}
if creator.blockSigVerifier == nil {
return errors.New("nil block signature verifier")
}
return creator.ClusterVerifyBlocks(blocks, creator.blockSigVerifier, creator.vb)
}