github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/internal/peer/common/common.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package common
     8  
     9  import (
    10  	"context"
    11  	"crypto/tls"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"os"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/golang/protobuf/proto"
    19  	"github.com/hechain20/hechain/bccsp"
    20  	"github.com/hechain20/hechain/bccsp/factory"
    21  	"github.com/hechain20/hechain/common/channelconfig"
    22  	"github.com/hechain20/hechain/common/flogging"
    23  	"github.com/hechain20/hechain/core/config"
    24  	"github.com/hechain20/hechain/core/scc/cscc"
    25  	"github.com/hechain20/hechain/internal/pkg/comm"
    26  	"github.com/hechain20/hechain/msp"
    27  	mspmgmt "github.com/hechain20/hechain/msp/mgmt"
    28  	"github.com/hechain20/hechain/protoutil"
    29  	pcommon "github.com/hyperledger/fabric-protos-go/common"
    30  	pb "github.com/hyperledger/fabric-protos-go/peer"
    31  	"github.com/pkg/errors"
    32  	"github.com/spf13/cobra"
    33  	"github.com/spf13/viper"
    34  	grpc "google.golang.org/grpc"
    35  )
    36  
    37  // UndefinedParamValue defines what undefined parameters in the command line will initialise to
    38  const (
    39  	UndefinedParamValue = ""
    40  	CmdRoot             = "core"
    41  )
    42  
    43  var (
    44  	mainLogger = flogging.MustGetLogger("main")
    45  	logOutput  = os.Stderr
    46  )
    47  
    48  var (
    49  	defaultConnTimeout = 3 * time.Second
    50  	// These function variables (xyzFnc) can be used to invoke corresponding xyz function
    51  	// this will allow the invoking packages to mock these functions in their unit test cases
    52  
    53  	// GetEndorserClientFnc is a function that returns a new endorser client connection
    54  	// to the provided peer address using the TLS root cert file,
    55  	// by default it is set to GetEndorserClient function
    56  	GetEndorserClientFnc func(address, tlsRootCertFile string) (pb.EndorserClient, error)
    57  
    58  	// GetPeerDeliverClientFnc is a function that returns a new deliver client connection
    59  	// to the provided peer address using the TLS root cert file,
    60  	// by default it is set to GetDeliverClient function
    61  	GetPeerDeliverClientFnc func(address, tlsRootCertFile string) (pb.DeliverClient, error)
    62  
    63  	// GetDeliverClientFnc is a function that returns a new deliver client connection
    64  	// to the provided peer address using the TLS root cert file,
    65  	// by default it is set to GetDeliverClient function
    66  	GetDeliverClientFnc func(address, tlsRootCertFile string) (pb.Deliver_DeliverClient, error)
    67  
    68  	// GetDefaultSignerFnc is a function that returns a default Signer(Default/PERR)
    69  	// by default it is set to GetDefaultSigner function
    70  	GetDefaultSignerFnc func() (msp.SigningIdentity, error)
    71  
    72  	// GetBroadcastClientFnc returns an instance of the BroadcastClient interface
    73  	// by default it is set to GetBroadcastClient function
    74  	GetBroadcastClientFnc func() (BroadcastClient, error)
    75  
    76  	// GetOrdererEndpointOfChainFnc returns orderer endpoints of given chain
    77  	// by default it is set to GetOrdererEndpointOfChain function
    78  	GetOrdererEndpointOfChainFnc func(chainID string, signer Signer,
    79  		endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error)
    80  
    81  	// GetClientCertificateFnc is a function that returns the client TLS certificate
    82  	GetClientCertificateFnc func() (tls.Certificate, error)
    83  )
    84  
    85  type CommonClient struct {
    86  	clientConfig comm.ClientConfig
    87  	address      string
    88  }
    89  
    90  func newCommonClient(address string, clientConfig comm.ClientConfig) (*CommonClient, error) {
    91  	return &CommonClient{
    92  		clientConfig: clientConfig,
    93  		address:      address,
    94  	}, nil
    95  }
    96  
    97  func (cc *CommonClient) Certificate() tls.Certificate {
    98  	if !cc.clientConfig.SecOpts.RequireClientCert {
    99  		return tls.Certificate{}
   100  	}
   101  	cert, err := cc.clientConfig.SecOpts.ClientCertificate()
   102  	if err != nil {
   103  		panic(err)
   104  	}
   105  	return cert
   106  }
   107  
   108  // Dial will create a new gRPC client connection to the provided
   109  // address. The options used for the dial are sourced from the
   110  // ClientConfig provided to the constructor.
   111  func (cc *CommonClient) Dial(address string) (*grpc.ClientConn, error) {
   112  	return cc.clientConfig.Dial(address)
   113  }
   114  
   115  func init() {
   116  	GetEndorserClientFnc = GetEndorserClient
   117  	GetDefaultSignerFnc = GetDefaultSigner
   118  	GetBroadcastClientFnc = GetBroadcastClient
   119  	GetOrdererEndpointOfChainFnc = GetOrdererEndpointOfChain
   120  	GetDeliverClientFnc = GetDeliverClient
   121  	GetPeerDeliverClientFnc = GetPeerDeliverClient
   122  	GetClientCertificateFnc = GetClientCertificate
   123  }
   124  
   125  // InitConfig initializes viper config
   126  func InitConfig(cmdRoot string) error {
   127  	err := config.InitViper(nil, cmdRoot)
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	err = viper.ReadInConfig() // Find and read the config file
   133  	if err != nil {            // Handle errors reading the config file
   134  		// The version of Viper we use claims the config type isn't supported when in fact the file hasn't been found
   135  		// Display a more helpful message to avoid confusing the user.
   136  		if strings.Contains(fmt.Sprint(err), "Unsupported Config Type") {
   137  			return errors.New(fmt.Sprintf("Could not find config file. "+
   138  				"Please make sure that FABRIC_CFG_PATH is set to a path "+
   139  				"which contains %s.yaml", cmdRoot))
   140  		} else {
   141  			return errors.WithMessagef(err, "error when reading %s config file", cmdRoot)
   142  		}
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // InitCrypto initializes crypto for this peer
   149  func InitCrypto(mspMgrConfigDir, localMSPID, localMSPType string) error {
   150  	// Check whether msp folder exists
   151  	fi, err := os.Stat(mspMgrConfigDir)
   152  	if err != nil {
   153  		return errors.Errorf("cannot init crypto, specified path \"%s\" does not exist or cannot be accessed: %v", mspMgrConfigDir, err)
   154  	} else if !fi.IsDir() {
   155  		return errors.Errorf("cannot init crypto, specified path \"%s\" is not a directory", mspMgrConfigDir)
   156  	}
   157  	// Check whether localMSPID exists
   158  	if localMSPID == "" {
   159  		return errors.New("the local MSP must have an ID")
   160  	}
   161  
   162  	// Init the BCCSP
   163  	SetBCCSPKeystorePath()
   164  	bccspConfig := factory.GetDefaultOpts()
   165  	if err := viper.UnmarshalKey("peer.BCCSP", &bccspConfig); err != nil {
   166  		return errors.WithMessage(err, "could not decode peer BCCSP configuration")
   167  	}
   168  
   169  	conf, err := msp.GetLocalMspConfigWithType(mspMgrConfigDir, bccspConfig, localMSPID, localMSPType)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	err = mspmgmt.GetLocalMSP(factory.GetDefault()).Setup(conf)
   174  	if err != nil {
   175  		return errors.WithMessagef(err, "error when setting up MSP of type %s from directory %s", localMSPType, mspMgrConfigDir)
   176  	}
   177  
   178  	return nil
   179  }
   180  
   181  // SetBCCSPKeystorePath sets the file keystore path for the SW BCCSP provider
   182  // to an absolute path relative to the config file.
   183  func SetBCCSPKeystorePath() {
   184  	key := "peer.BCCSP.SW.FileKeyStore.KeyStore"
   185  	if ksPath := config.GetPath(key); ksPath != "" {
   186  		viper.Set(key, ksPath)
   187  	}
   188  }
   189  
   190  // GetDefaultSigner return a default Signer(Default/PEER) for cli
   191  func GetDefaultSigner() (msp.SigningIdentity, error) {
   192  	signer, err := mspmgmt.GetLocalMSP(factory.GetDefault()).GetDefaultSigningIdentity()
   193  	if err != nil {
   194  		return nil, errors.WithMessage(err, "error obtaining the default signing identity")
   195  	}
   196  
   197  	return signer, err
   198  }
   199  
   200  // Signer defines the interface needed for signing messages
   201  type Signer interface {
   202  	Sign(msg []byte) ([]byte, error)
   203  	Serialize() ([]byte, error)
   204  }
   205  
   206  // GetOrdererEndpointOfChain returns orderer endpoints of given chain
   207  func GetOrdererEndpointOfChain(chainID string, signer Signer, endorserClient pb.EndorserClient, cryptoProvider bccsp.BCCSP) ([]string, error) {
   208  	// query cscc for chain config block
   209  	invocation := &pb.ChaincodeInvocationSpec{
   210  		ChaincodeSpec: &pb.ChaincodeSpec{
   211  			Type:        pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]),
   212  			ChaincodeId: &pb.ChaincodeID{Name: "cscc"},
   213  			Input:       &pb.ChaincodeInput{Args: [][]byte{[]byte(cscc.GetChannelConfig), []byte(chainID)}},
   214  		},
   215  	}
   216  
   217  	creator, err := signer.Serialize()
   218  	if err != nil {
   219  		return nil, errors.WithMessage(err, "error serializing identity for signer")
   220  	}
   221  
   222  	prop, _, err := protoutil.CreateProposalFromCIS(pcommon.HeaderType_CONFIG, "", invocation, creator)
   223  	if err != nil {
   224  		return nil, errors.WithMessage(err, "error creating GetChannelConfig proposal")
   225  	}
   226  
   227  	signedProp, err := protoutil.GetSignedProposal(prop, signer)
   228  	if err != nil {
   229  		return nil, errors.WithMessage(err, "error creating signed GetChannelConfig proposal")
   230  	}
   231  
   232  	proposalResp, err := endorserClient.ProcessProposal(context.Background(), signedProp)
   233  	if err != nil {
   234  		return nil, errors.WithMessage(err, "error endorsing GetChannelConfig")
   235  	}
   236  
   237  	if proposalResp == nil {
   238  		return nil, errors.New("received nil proposal response")
   239  	}
   240  
   241  	if proposalResp.Response.Status != 0 && proposalResp.Response.Status != 200 {
   242  		return nil, errors.Errorf("error bad proposal response %d: %s", proposalResp.Response.Status, proposalResp.Response.Message)
   243  	}
   244  
   245  	// parse config
   246  	channelConfig := &pcommon.Config{}
   247  	if err := proto.Unmarshal(proposalResp.Response.Payload, channelConfig); err != nil {
   248  		return nil, errors.WithMessage(err, "error unmarshalling channel config")
   249  	}
   250  
   251  	bundle, err := channelconfig.NewBundle(chainID, channelConfig, cryptoProvider)
   252  	if err != nil {
   253  		return nil, errors.WithMessage(err, "error loading channel config")
   254  	}
   255  
   256  	return bundle.ChannelConfig().OrdererAddresses(), nil
   257  }
   258  
   259  // CheckLogLevel checks that a given log level string is valid
   260  func CheckLogLevel(level string) error {
   261  	if !flogging.IsValidLevel(level) {
   262  		return errors.Errorf("invalid log level provided - %s", level)
   263  	}
   264  	return nil
   265  }
   266  
   267  func configFromEnv(prefix string) (address string, clientConfig comm.ClientConfig, err error) {
   268  	address = viper.GetString(prefix + ".address")
   269  	clientConfig = comm.ClientConfig{}
   270  	connTimeout := viper.GetDuration(prefix + ".client.connTimeout")
   271  	if connTimeout == time.Duration(0) {
   272  		connTimeout = defaultConnTimeout
   273  	}
   274  	clientConfig.DialTimeout = connTimeout
   275  	secOpts := comm.SecureOptions{
   276  		UseTLS:             viper.GetBool(prefix + ".tls.enabled"),
   277  		RequireClientCert:  viper.GetBool(prefix + ".tls.clientAuthRequired"),
   278  		TimeShift:          viper.GetDuration(prefix + ".tls.handshakeTimeShift"),
   279  		ServerNameOverride: viper.GetString(prefix + ".tls.serverhostoverride"),
   280  	}
   281  	if secOpts.UseTLS {
   282  		caPEM, res := ioutil.ReadFile(config.GetPath(prefix + ".tls.rootcert.file"))
   283  		if res != nil {
   284  			err = errors.WithMessagef(res, "unable to load %s.tls.rootcert.file", prefix)
   285  			return
   286  		}
   287  		secOpts.ServerRootCAs = [][]byte{caPEM}
   288  	}
   289  	if secOpts.RequireClientCert {
   290  		secOpts.Key, secOpts.Certificate, err = getClientAuthInfoFromEnv(prefix)
   291  		if err != nil {
   292  			return
   293  		}
   294  	}
   295  	clientConfig.SecOpts = secOpts
   296  	clientConfig.MaxRecvMsgSize = comm.DefaultMaxRecvMsgSize
   297  	if viper.IsSet(prefix + ".maxRecvMsgSize") {
   298  		clientConfig.MaxRecvMsgSize = int(viper.GetInt32(prefix + ".maxRecvMsgSize"))
   299  	}
   300  	clientConfig.MaxSendMsgSize = comm.DefaultMaxSendMsgSize
   301  	if viper.IsSet(prefix + ".maxSendMsgSize") {
   302  		clientConfig.MaxSendMsgSize = int(viper.GetInt32(prefix + ".maxSendMsgSize"))
   303  	}
   304  	return
   305  }
   306  
   307  // getClientAuthInfoFromEnv reads client tls key file and cert file and returns the bytes for the files
   308  func getClientAuthInfoFromEnv(prefix string) ([]byte, []byte, error) {
   309  	keyPEM, err := ioutil.ReadFile(config.GetPath(prefix + ".tls.clientKey.file"))
   310  	if err != nil {
   311  		return nil, nil, errors.WithMessagef(err, "unable to load %s.tls.clientKey.file", prefix)
   312  	}
   313  	certPEM, err := ioutil.ReadFile(config.GetPath(prefix + ".tls.clientCert.file"))
   314  	if err != nil {
   315  		return nil, nil, errors.WithMessagef(err, "unable to load %s.tls.clientCert.file", prefix)
   316  	}
   317  
   318  	return keyPEM, certPEM, nil
   319  }
   320  
   321  func InitCmd(cmd *cobra.Command, args []string) {
   322  	err := InitConfig(CmdRoot)
   323  	if err != nil { // Handle errors reading the config file
   324  		mainLogger.Errorf("Fatal error when initializing %s config : %s", CmdRoot, err)
   325  		os.Exit(1)
   326  	}
   327  
   328  	// read in the legacy logging level settings and, if set,
   329  	// notify users of the FABRIC_LOGGING_SPEC env variable
   330  	var loggingLevel string
   331  	if viper.GetString("logging_level") != "" {
   332  		loggingLevel = viper.GetString("logging_level")
   333  	} else {
   334  		loggingLevel = viper.GetString("logging.level")
   335  	}
   336  	if loggingLevel != "" {
   337  		mainLogger.Warning("CORE_LOGGING_LEVEL is no longer supported, please use the FABRIC_LOGGING_SPEC environment variable")
   338  	}
   339  
   340  	loggingSpec := os.Getenv("FABRIC_LOGGING_SPEC")
   341  	loggingFormat := os.Getenv("FABRIC_LOGGING_FORMAT")
   342  
   343  	flogging.Init(flogging.Config{
   344  		Format:  loggingFormat,
   345  		Writer:  logOutput,
   346  		LogSpec: loggingSpec,
   347  	})
   348  
   349  	// chaincode packaging does not require material from the local MSP
   350  	if cmd.CommandPath() == "peer lifecycle chaincode package" || cmd.CommandPath() == "peer lifecycle chaincode calculatepackageid" {
   351  		mainLogger.Debug("peer lifecycle chaincode package does not need to init crypto")
   352  		return
   353  	}
   354  
   355  	// Init the MSP
   356  	mspMgrConfigDir := config.GetPath("peer.mspConfigPath")
   357  	mspID := viper.GetString("peer.localMspId")
   358  	mspType := viper.GetString("peer.localMspType")
   359  	if mspType == "" {
   360  		mspType = msp.ProviderTypeToString(msp.FABRIC)
   361  	}
   362  	err = InitCrypto(mspMgrConfigDir, mspID, mspType)
   363  	if err != nil { // Handle errors reading the config file
   364  		mainLogger.Errorf("Cannot run peer because %s", err.Error())
   365  		os.Exit(1)
   366  	}
   367  }