/* Copyright IBM Corp. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ package channel import ( "strings" "time" cb "github.com/hyperledger/fabric-protos-go/common" pb "github.com/hyperledger/fabric-protos-go/peer" "github.com/hyperledger/fabric/common/flogging" "github.com/hyperledger/fabric/internal/peer/common" "github.com/hyperledger/fabric/msp" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" ) var logger = flogging.MustGetLogger("channelCmd") const ( EndorserRequired bool = true EndorserNotRequired bool = false OrdererRequired bool = true OrdererNotRequired bool = false PeerDeliverRequired bool = true PeerDeliverNotRequired bool = false ) var ( // join related variables. genesisBlockPath string // joinbysnapshot related variables snapshotPath string // create related variables channelID string channelTxFile string outputBlock string timeout time.Duration // fetch related variables bestEffort bool ) // Cmd returns the cobra command for Node func Cmd(cf *ChannelCmdFactory) *cobra.Command { AddFlags(channelCmd) channelCmd.AddCommand(createCmd(cf)) channelCmd.AddCommand(fetchCmd(cf)) channelCmd.AddCommand(joinCmd(cf)) channelCmd.AddCommand(joinBySnapshotCmd(cf)) channelCmd.AddCommand(joinBySnapshotStatusCmd(cf)) channelCmd.AddCommand(listCmd(cf)) channelCmd.AddCommand(updateCmd(cf)) channelCmd.AddCommand(signconfigtxCmd(cf)) channelCmd.AddCommand(getinfoCmd(cf)) return channelCmd } // AddFlags adds flags for create and join func AddFlags(cmd *cobra.Command) { common.AddOrdererFlags(cmd) } var flags *pflag.FlagSet func init() { resetFlags() } // Explicitly define a method to facilitate tests func resetFlags() { flags = &pflag.FlagSet{} flags.StringVarP(&genesisBlockPath, "blockpath", "b", common.UndefinedParamValue, "Path to file containing genesis block") flags.StringVarP(&snapshotPath, "snapshotpath", "", common.UndefinedParamValue, "Path to the snapshot directory") flags.StringVarP(&channelID, "channelID", "c", common.UndefinedParamValue, "In case of a newChain command, the channel ID to create. It must be all lower case, less than 250 characters long and match the regular expression: [a-z][a-z0-9.-]*") flags.StringVarP(&channelTxFile, "file", "f", "", "Configuration transaction file generated by a tool such as configtxgen for submitting to orderer") flags.StringVarP(&outputBlock, "outputBlock", "", common.UndefinedParamValue, `The path to write the genesis block for the channel. (default ./.block)`) flags.DurationVarP(&timeout, "timeout", "t", 10*time.Second, "Channel creation timeout") flags.BoolVarP(&bestEffort, "bestEffort", "", false, "Whether fetch requests should ignore errors and return blocks on a best effort basis") } func attachFlags(cmd *cobra.Command, names []string) { cmdFlags := cmd.Flags() for _, name := range names { if flag := flags.Lookup(name); flag != nil { cmdFlags.AddFlag(flag) } else { logger.Fatalf("Could not find flag '%s' to attach to commond '%s'", name, cmd.Name()) } } } var channelCmd = &cobra.Command{ Use: "channel", Short: "Operate a channel: create|fetch|join|joinbysnapshot|joinbysnapshotstatus|list|update|signconfigtx|getinfo.", Long: "Operate a channel: create|fetch|join|joinbysnapshot|joinbysnapshotstatus|list|update|signconfigtx|getinfo.", PersistentPreRun: func(cmd *cobra.Command, args []string) { common.InitCmd(cmd, args) common.SetOrdererEnv(cmd, args) }, } type BroadcastClientFactory func() (common.BroadcastClient, error) type deliverClientIntf interface { GetSpecifiedBlock(num uint64) (*cb.Block, error) GetOldestBlock() (*cb.Block, error) GetNewestBlock() (*cb.Block, error) Close() error } // ChannelCmdFactory holds the clients used by ChannelCmdFactory type ChannelCmdFactory struct { EndorserClient pb.EndorserClient Signer msp.SigningIdentity BroadcastClient common.BroadcastClient DeliverClient deliverClientIntf BroadcastFactory BroadcastClientFactory } // InitCmdFactory init the ChannelCmdFactory with clients to endorser and orderer according to params func InitCmdFactory(isEndorserRequired, isPeerDeliverRequired, isOrdererRequired bool) (*ChannelCmdFactory, error) { if isPeerDeliverRequired && isOrdererRequired { // this is likely a bug during development caused by adding a new cmd return nil, errors.New("ERROR - only a single deliver source is currently supported") } var err error cf := &ChannelCmdFactory{} cf.Signer, err = common.GetDefaultSignerFnc() if err != nil { return nil, errors.WithMessage(err, "error getting default signer") } cf.BroadcastFactory = func() (common.BroadcastClient, error) { return common.GetBroadcastClientFnc() } // for join and list, we need the endorser as well if isEndorserRequired { // creating an EndorserClient with these empty parameters will create a // connection using the values of "peer.address" and // "peer.tls.rootcert.file" cf.EndorserClient, err = common.GetEndorserClientFnc(common.UndefinedParamValue, common.UndefinedParamValue) if err != nil { return nil, errors.WithMessage(err, "error getting endorser client for channel") } } // for fetching blocks from a peer if isPeerDeliverRequired { cf.DeliverClient, err = common.NewDeliverClientForPeer(channelID, cf.Signer, bestEffort) if err != nil { return nil, errors.WithMessage(err, "error getting deliver client for channel") } } // for create and fetch, we need the orderer as well if isOrdererRequired { if len(strings.Split(common.OrderingEndpoint, ":")) != 2 { return nil, errors.Errorf("ordering service endpoint %s is not valid or missing", common.OrderingEndpoint) } cf.DeliverClient, err = common.NewDeliverClientForOrderer(channelID, cf.Signer, bestEffort) if err != nil { return nil, err } } logger.Infof("Endorser and orderer connections initialized") return cf, nil }