github.com/true-sqn/fabric@v2.1.1+incompatible/internal/peer/channel/create.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package channel
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"time"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	cb "github.com/hyperledger/fabric-protos-go/common"
    16  	"github.com/hyperledger/fabric/common/configtx"
    17  	"github.com/hyperledger/fabric/common/util"
    18  	"github.com/hyperledger/fabric/internal/configtxgen/encoder"
    19  	"github.com/hyperledger/fabric/internal/configtxgen/genesisconfig"
    20  	"github.com/hyperledger/fabric/internal/peer/common"
    21  	"github.com/hyperledger/fabric/internal/pkg/identity"
    22  	"github.com/hyperledger/fabric/protoutil"
    23  	"github.com/pkg/errors"
    24  	"github.com/spf13/cobra"
    25  )
    26  
    27  //ConfigTxFileNotFound channel create configuration tx file not found
    28  type ConfigTxFileNotFound string
    29  
    30  func (e ConfigTxFileNotFound) Error() string {
    31  	return fmt.Sprintf("channel create configuration tx file not found %s", string(e))
    32  }
    33  
    34  //InvalidCreateTx invalid channel create transaction
    35  type InvalidCreateTx string
    36  
    37  func (e InvalidCreateTx) Error() string {
    38  	return fmt.Sprintf("Invalid channel create transaction : %s", string(e))
    39  }
    40  
    41  func createCmd(cf *ChannelCmdFactory) *cobra.Command {
    42  	createCmd := &cobra.Command{
    43  		Use:   "create",
    44  		Short: "Create a channel",
    45  		Long:  "Create a channel and write the genesis block to a file.",
    46  		RunE: func(cmd *cobra.Command, args []string) error {
    47  			return create(cmd, args, cf)
    48  		},
    49  	}
    50  	flagList := []string{
    51  		"channelID",
    52  		"file",
    53  		"outputBlock",
    54  		"timeout",
    55  	}
    56  	attachFlags(createCmd, flagList)
    57  
    58  	return createCmd
    59  }
    60  
    61  func createChannelFromDefaults(cf *ChannelCmdFactory) (*cb.Envelope, error) {
    62  	chCrtEnv, err := encoder.MakeChannelCreationTransaction(
    63  		channelID,
    64  		cf.Signer,
    65  		genesisconfig.Load(genesisconfig.SampleSingleMSPChannelProfile),
    66  	)
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return chCrtEnv, nil
    72  }
    73  
    74  func createChannelFromConfigTx(configTxFileName string) (*cb.Envelope, error) {
    75  	cftx, err := ioutil.ReadFile(configTxFileName)
    76  	if err != nil {
    77  		return nil, ConfigTxFileNotFound(err.Error())
    78  	}
    79  
    80  	return protoutil.UnmarshalEnvelope(cftx)
    81  }
    82  
    83  func sanityCheckAndSignConfigTx(envConfigUpdate *cb.Envelope, signer identity.SignerSerializer) (*cb.Envelope, error) {
    84  	payload, err := protoutil.UnmarshalPayload(envConfigUpdate.Payload)
    85  	if err != nil {
    86  		return nil, InvalidCreateTx("bad payload")
    87  	}
    88  
    89  	if payload.Header == nil || payload.Header.ChannelHeader == nil {
    90  		return nil, InvalidCreateTx("bad header")
    91  	}
    92  
    93  	ch, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
    94  	if err != nil {
    95  		return nil, InvalidCreateTx("could not unmarshall channel header")
    96  	}
    97  
    98  	if ch.Type != int32(cb.HeaderType_CONFIG_UPDATE) {
    99  		return nil, InvalidCreateTx("bad type")
   100  	}
   101  
   102  	if ch.ChannelId == "" {
   103  		return nil, InvalidCreateTx("empty channel id")
   104  	}
   105  
   106  	// Specifying the chainID on the CLI is usually redundant, as a hack, set it
   107  	// here if it has not been set explicitly
   108  	if channelID == "" {
   109  		channelID = ch.ChannelId
   110  	}
   111  
   112  	if ch.ChannelId != channelID {
   113  		return nil, InvalidCreateTx(fmt.Sprintf("mismatched channel ID %s != %s", ch.ChannelId, channelID))
   114  	}
   115  
   116  	configUpdateEnv, err := configtx.UnmarshalConfigUpdateEnvelope(payload.Data)
   117  	if err != nil {
   118  		return nil, InvalidCreateTx("Bad config update env")
   119  	}
   120  
   121  	sigHeader, err := protoutil.NewSignatureHeader(signer)
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  
   126  	configSig := &cb.ConfigSignature{
   127  		SignatureHeader: protoutil.MarshalOrPanic(sigHeader),
   128  	}
   129  
   130  	configSig.Signature, err = signer.Sign(util.ConcatenateBytes(configSig.SignatureHeader, configUpdateEnv.ConfigUpdate))
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	configUpdateEnv.Signatures = append(configUpdateEnv.Signatures, configSig)
   136  
   137  	return protoutil.CreateSignedEnvelope(cb.HeaderType_CONFIG_UPDATE, channelID, signer, configUpdateEnv, 0, 0)
   138  }
   139  
   140  func sendCreateChainTransaction(cf *ChannelCmdFactory) error {
   141  	var err error
   142  	var chCrtEnv *cb.Envelope
   143  
   144  	if channelTxFile != "" {
   145  		if chCrtEnv, err = createChannelFromConfigTx(channelTxFile); err != nil {
   146  			return err
   147  		}
   148  	} else {
   149  		if chCrtEnv, err = createChannelFromDefaults(cf); err != nil {
   150  			return err
   151  		}
   152  	}
   153  
   154  	if chCrtEnv, err = sanityCheckAndSignConfigTx(chCrtEnv, cf.Signer); err != nil {
   155  		return err
   156  	}
   157  
   158  	var broadcastClient common.BroadcastClient
   159  	broadcastClient, err = cf.BroadcastFactory()
   160  	if err != nil {
   161  		return errors.WithMessage(err, "error getting broadcast client")
   162  	}
   163  
   164  	defer broadcastClient.Close()
   165  	err = broadcastClient.Send(chCrtEnv)
   166  
   167  	return err
   168  }
   169  
   170  func executeCreate(cf *ChannelCmdFactory) error {
   171  	err := sendCreateChainTransaction(cf)
   172  	if err != nil {
   173  		return err
   174  	}
   175  
   176  	block, err := getGenesisBlock(cf)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	b, err := proto.Marshal(block)
   182  	if err != nil {
   183  		return err
   184  	}
   185  
   186  	file := channelID + ".block"
   187  	if outputBlock != common.UndefinedParamValue {
   188  		file = outputBlock
   189  	}
   190  	err = ioutil.WriteFile(file, b, 0644)
   191  	if err != nil {
   192  		return err
   193  	}
   194  
   195  	return nil
   196  }
   197  
   198  func getGenesisBlock(cf *ChannelCmdFactory) (*cb.Block, error) {
   199  	timer := time.NewTimer(timeout)
   200  	defer timer.Stop()
   201  
   202  	for {
   203  		select {
   204  		case <-timer.C:
   205  			cf.DeliverClient.Close()
   206  			return nil, errors.New("timeout waiting for channel creation")
   207  		default:
   208  			if block, err := cf.DeliverClient.GetSpecifiedBlock(0); err != nil {
   209  				cf.DeliverClient.Close()
   210  				cf, err = InitCmdFactory(EndorserNotRequired, PeerDeliverNotRequired, OrdererRequired)
   211  				if err != nil {
   212  					return nil, errors.WithMessage(err, "failed connecting")
   213  				}
   214  				time.Sleep(200 * time.Millisecond)
   215  			} else {
   216  				cf.DeliverClient.Close()
   217  				return block, nil
   218  			}
   219  		}
   220  	}
   221  }
   222  
   223  func create(cmd *cobra.Command, args []string, cf *ChannelCmdFactory) error {
   224  	// the global chainID filled by the "-c" command
   225  	if channelID == common.UndefinedParamValue {
   226  		return errors.New("must supply channel ID")
   227  	}
   228  
   229  	// Parsing of the command line is done so silence cmd usage
   230  	cmd.SilenceUsage = true
   231  
   232  	var err error
   233  	if cf == nil {
   234  		cf, err = InitCmdFactory(EndorserNotRequired, PeerDeliverNotRequired, OrdererRequired)
   235  		if err != nil {
   236  			return err
   237  		}
   238  	}
   239  	return executeCreate(cf)
   240  }