github.com/defanghe/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 }