github.com/suchongming/fabric@v2.1.1+incompatible/integration/nwo/configblock.go (about) 1 /* 2 Copyright IBM Corp All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package nwo 8 9 import ( 10 "io/ioutil" 11 "os" 12 "path/filepath" 13 "strings" 14 15 "github.com/golang/protobuf/proto" 16 "github.com/hyperledger/fabric-protos-go/common" 17 "github.com/hyperledger/fabric-protos-go/msp" 18 protosorderer "github.com/hyperledger/fabric-protos-go/orderer" 19 "github.com/hyperledger/fabric/integration/nwo/commands" 20 "github.com/hyperledger/fabric/internal/configtxlator/update" 21 "github.com/hyperledger/fabric/protoutil" 22 . "github.com/onsi/gomega" 23 "github.com/onsi/gomega/gbytes" 24 "github.com/onsi/gomega/gexec" 25 ) 26 27 // GetConfigBlock retrieves the current config block for a channel. 28 func GetConfigBlock(n *Network, peer *Peer, orderer *Orderer, channel string) *common.Block { 29 tempDir, err := ioutil.TempDir(n.RootDir, "getConfigBlock") 30 Expect(err).NotTo(HaveOccurred()) 31 defer os.RemoveAll(tempDir) 32 33 // fetch the config block 34 output := filepath.Join(tempDir, "config_block.pb") 35 sess, err := n.OrdererAdminSession(orderer, peer, commands.ChannelFetch{ 36 ChannelID: channel, 37 Block: "config", 38 Orderer: n.OrdererAddress(orderer, ListenPort), 39 OutputFile: output, 40 ClientAuth: n.ClientAuthRequired, 41 }) 42 Expect(err).NotTo(HaveOccurred()) 43 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 44 Expect(sess.Err).To(gbytes.Say("Received block: ")) 45 46 // unmarshal the config block bytes 47 configBlock := UnmarshalBlockFromFile(output) 48 return configBlock 49 } 50 51 // GetConfig retrieves the last config of the given channel. 52 func GetConfig(n *Network, peer *Peer, orderer *Orderer, channel string) *common.Config { 53 configBlock := GetConfigBlock(n, peer, orderer, channel) 54 // unmarshal the envelope bytes 55 envelope, err := protoutil.GetEnvelopeFromBlock(configBlock.Data.Data[0]) 56 Expect(err).NotTo(HaveOccurred()) 57 58 // unmarshal the payload bytes 59 payload, err := protoutil.UnmarshalPayload(envelope.Payload) 60 Expect(err).NotTo(HaveOccurred()) 61 62 // unmarshal the config envelope bytes 63 configEnv := &common.ConfigEnvelope{} 64 err = proto.Unmarshal(payload.Data, configEnv) 65 Expect(err).NotTo(HaveOccurred()) 66 67 // clone the config 68 return configEnv.Config 69 } 70 71 // UpdateConfig computes, signs, and submits a configuration update and waits 72 // for the update to complete. 73 func UpdateConfig(n *Network, orderer *Orderer, channel string, current, updated *common.Config, getConfigBlockFromOrderer bool, submitter *Peer, additionalSigners ...*Peer) { 74 tempDir, err := ioutil.TempDir("", "updateConfig") 75 Expect(err).NotTo(HaveOccurred()) 76 defer os.RemoveAll(tempDir) 77 78 // compute update 79 configUpdate, err := update.Compute(current, updated) 80 Expect(err).NotTo(HaveOccurred()) 81 configUpdate.ChannelId = channel 82 83 signedEnvelope, err := protoutil.CreateSignedEnvelope( 84 common.HeaderType_CONFIG_UPDATE, 85 channel, 86 nil, // local signer 87 &common.ConfigUpdateEnvelope{ConfigUpdate: protoutil.MarshalOrPanic(configUpdate)}, 88 0, // message version 89 0, // epoch 90 ) 91 Expect(err).NotTo(HaveOccurred()) 92 Expect(signedEnvelope).NotTo(BeNil()) 93 94 updateFile := filepath.Join(tempDir, "update.pb") 95 err = ioutil.WriteFile(updateFile, protoutil.MarshalOrPanic(signedEnvelope), 0600) 96 Expect(err).NotTo(HaveOccurred()) 97 98 for _, signer := range additionalSigners { 99 sess, err := n.PeerAdminSession(signer, commands.SignConfigTx{ 100 File: updateFile, 101 ClientAuth: n.ClientAuthRequired, 102 }) 103 Expect(err).NotTo(HaveOccurred()) 104 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 105 } 106 107 var currentBlockNumber uint64 108 // get current configuration block number 109 if getConfigBlockFromOrderer { 110 currentBlockNumber = CurrentConfigBlockNumber(n, submitter, orderer, channel) 111 } else { 112 currentBlockNumber = CurrentConfigBlockNumber(n, submitter, nil, channel) 113 } 114 115 sess, err := n.PeerAdminSession(submitter, commands.ChannelUpdate{ 116 ChannelID: channel, 117 Orderer: n.OrdererAddress(orderer, ListenPort), 118 File: updateFile, 119 ClientAuth: n.ClientAuthRequired, 120 }) 121 Expect(err).NotTo(HaveOccurred()) 122 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 123 Expect(sess.Err).To(gbytes.Say("Successfully submitted channel update")) 124 125 if getConfigBlockFromOrderer { 126 ccb := func() uint64 { return CurrentConfigBlockNumber(n, submitter, orderer, channel) } 127 Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber)) 128 return 129 } 130 // wait for the block to be committed to all peers that 131 // have joined the channel 132 for _, peer := range n.PeersWithChannel(channel) { 133 ccb := func() uint64 { return CurrentConfigBlockNumber(n, peer, nil, channel) } 134 Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber)) 135 } 136 } 137 138 // CurrentConfigBlockNumber retrieves the block number from the header of the 139 // current config block. This can be used to detect when configuration change 140 // has completed. If an orderer is not provided, the current config block will 141 // be fetched from the peer. 142 func CurrentConfigBlockNumber(n *Network, peer *Peer, orderer *Orderer, channel string) uint64 { 143 tempDir, err := ioutil.TempDir(n.RootDir, "currentConfigBlock") 144 Expect(err).NotTo(HaveOccurred()) 145 defer os.RemoveAll(tempDir) 146 147 // fetch the config block 148 output := filepath.Join(tempDir, "config_block.pb") 149 if orderer == nil { 150 return CurrentConfigBlockNumberFromPeer(n, peer, channel, output) 151 } 152 153 FetchConfigBlock(n, peer, orderer, channel, output) 154 155 // unmarshal the config block bytes 156 configBlock := UnmarshalBlockFromFile(output) 157 158 return configBlock.Header.Number 159 } 160 161 // CurrentConfigBlockNumberFromPeer retrieves the block number from the header 162 // of the peer's current config block. 163 func CurrentConfigBlockNumberFromPeer(n *Network, peer *Peer, channel, output string) uint64 { 164 sess, err := n.PeerAdminSession(peer, commands.ChannelFetch{ 165 ChannelID: channel, 166 Block: "config", 167 OutputFile: output, 168 ClientAuth: n.ClientAuthRequired, 169 }) 170 Expect(err).NotTo(HaveOccurred()) 171 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 172 Expect(sess.Err).To(gbytes.Say("Received block: ")) 173 174 configBlock := UnmarshalBlockFromFile(output) 175 176 return configBlock.Header.Number 177 } 178 179 // FetchConfigBlock fetches latest config block. 180 func FetchConfigBlock(n *Network, peer *Peer, orderer *Orderer, channel string, output string) { 181 fetch := func() int { 182 sess, err := n.OrdererAdminSession(orderer, peer, commands.ChannelFetch{ 183 ChannelID: channel, 184 Block: "config", 185 Orderer: n.OrdererAddress(orderer, ListenPort), 186 OutputFile: output, 187 ClientAuth: n.ClientAuthRequired, 188 }) 189 Expect(err).NotTo(HaveOccurred()) 190 code := sess.Wait(n.EventuallyTimeout).ExitCode() 191 if code == 0 { 192 Expect(sess.Err).To(gbytes.Say("Received block: ")) 193 } 194 return code 195 } 196 Eventually(fetch, n.EventuallyTimeout).Should(Equal(0)) 197 } 198 199 // UpdateOrdererConfig computes, signs, and submits a configuration update 200 // which requires orderers signature and waits for the update to complete. 201 func UpdateOrdererConfig(n *Network, orderer *Orderer, channel string, current, updated *common.Config, submitter *Peer, additionalSigners ...*Orderer) { 202 tempDir, err := ioutil.TempDir(n.RootDir, "updateConfig") 203 Expect(err).NotTo(HaveOccurred()) 204 updateFile := filepath.Join(tempDir, "update.pb") 205 defer os.RemoveAll(tempDir) 206 207 currentBlockNumber := CurrentConfigBlockNumber(n, submitter, orderer, channel) 208 ComputeUpdateOrdererConfig(updateFile, n, channel, current, updated, submitter, additionalSigners...) 209 210 Eventually(func() bool { 211 sess, err := n.OrdererAdminSession(orderer, submitter, commands.ChannelUpdate{ 212 ChannelID: channel, 213 Orderer: n.OrdererAddress(orderer, ListenPort), 214 File: updateFile, 215 ClientAuth: n.ClientAuthRequired, 216 }) 217 Expect(err).NotTo(HaveOccurred()) 218 219 sess.Wait(n.EventuallyTimeout) 220 if sess.ExitCode() != 0 { 221 return false 222 } 223 224 return strings.Contains(string(sess.Err.Contents()), "Successfully submitted channel update") 225 }, n.EventuallyTimeout).Should(BeTrue()) 226 227 // wait for the block to be committed 228 ccb := func() uint64 { return CurrentConfigBlockNumber(n, submitter, orderer, channel) } 229 Eventually(ccb, n.EventuallyTimeout).Should(BeNumerically(">", currentBlockNumber)) 230 } 231 232 // UpdateOrdererConfigSession computes, signs, and submits a configuration 233 // update which requires orderer signatures. The caller should wait on the 234 // returned seession retrieve the exit code. 235 func UpdateOrdererConfigSession(n *Network, orderer *Orderer, channel string, current, updated *common.Config, submitter *Peer, additionalSigners ...*Orderer) *gexec.Session { 236 tempDir, err := ioutil.TempDir(n.RootDir, "updateConfig") 237 Expect(err).NotTo(HaveOccurred()) 238 updateFile := filepath.Join(tempDir, "update.pb") 239 240 ComputeUpdateOrdererConfig(updateFile, n, channel, current, updated, submitter, additionalSigners...) 241 242 // session should not return with a zero exit code nor with a success response 243 sess, err := n.OrdererAdminSession(orderer, submitter, commands.ChannelUpdate{ 244 ChannelID: channel, 245 Orderer: n.OrdererAddress(orderer, ListenPort), 246 File: updateFile, 247 ClientAuth: n.ClientAuthRequired, 248 }) 249 Expect(err).NotTo(HaveOccurred()) 250 return sess 251 } 252 253 func ComputeUpdateOrdererConfig(updateFile string, n *Network, channel string, current, updated *common.Config, submitter *Peer, additionalSigners ...*Orderer) { 254 // compute update 255 configUpdate, err := update.Compute(current, updated) 256 Expect(err).NotTo(HaveOccurred()) 257 configUpdate.ChannelId = channel 258 259 signedEnvelope, err := protoutil.CreateSignedEnvelope( 260 common.HeaderType_CONFIG_UPDATE, 261 channel, 262 nil, // local signer 263 &common.ConfigUpdateEnvelope{ConfigUpdate: protoutil.MarshalOrPanic(configUpdate)}, 264 0, // message version 265 0, // epoch 266 ) 267 Expect(err).NotTo(HaveOccurred()) 268 Expect(signedEnvelope).NotTo(BeNil()) 269 270 err = ioutil.WriteFile(updateFile, protoutil.MarshalOrPanic(signedEnvelope), 0600) 271 Expect(err).NotTo(HaveOccurred()) 272 273 for _, signer := range additionalSigners { 274 sess, err := n.OrdererAdminSession(signer, submitter, commands.SignConfigTx{File: updateFile}) 275 Expect(err).NotTo(HaveOccurred()) 276 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 277 } 278 } 279 280 // UnmarshalBlockFromFile unmarshals a proto encoded block from a file. 281 func UnmarshalBlockFromFile(blockFile string) *common.Block { 282 blockBytes, err := ioutil.ReadFile(blockFile) 283 Expect(err).NotTo(HaveOccurred()) 284 285 block, err := protoutil.UnmarshalBlock(blockBytes) 286 Expect(err).NotTo(HaveOccurred()) 287 288 return block 289 } 290 291 // ConsensusMetadataMutator receives ConsensusType.Metadata and mutates it. 292 type ConsensusMetadataMutator func([]byte) []byte 293 294 // MSPMutator receives FabricMSPConfig and mutates it. 295 type MSPMutator func(config msp.FabricMSPConfig) msp.FabricMSPConfig 296 297 // UpdateConsensusMetadata executes a config update that updates the consensus 298 // metadata according to the given ConsensusMetadataMutator. 299 func UpdateConsensusMetadata(network *Network, peer *Peer, orderer *Orderer, channel string, mutateMetadata ConsensusMetadataMutator) { 300 config := GetConfig(network, peer, orderer, channel) 301 updatedConfig := proto.Clone(config).(*common.Config) 302 303 consensusTypeConfigValue := updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"] 304 consensusTypeValue := &protosorderer.ConsensusType{} 305 err := proto.Unmarshal(consensusTypeConfigValue.Value, consensusTypeValue) 306 Expect(err).NotTo(HaveOccurred()) 307 308 consensusTypeValue.Metadata = mutateMetadata(consensusTypeValue.Metadata) 309 310 updatedConfig.ChannelGroup.Groups["Orderer"].Values["ConsensusType"] = &common.ConfigValue{ 311 ModPolicy: "Admins", 312 Value: protoutil.MarshalOrPanic(consensusTypeValue), 313 } 314 315 UpdateOrdererConfig(network, orderer, channel, config, updatedConfig, peer, orderer) 316 } 317 318 func UpdateOrdererMSP(network *Network, peer *Peer, orderer *Orderer, channel, orgID string, mutateMSP MSPMutator) { 319 config := GetConfig(network, peer, orderer, channel) 320 updatedConfig := proto.Clone(config).(*common.Config) 321 322 // Unpack the MSP config 323 rawMSPConfig := updatedConfig.ChannelGroup.Groups["Orderer"].Groups[orgID].Values["MSP"] 324 mspConfig := &msp.MSPConfig{} 325 err := proto.Unmarshal(rawMSPConfig.Value, mspConfig) 326 Expect(err).NotTo(HaveOccurred()) 327 328 fabricConfig := &msp.FabricMSPConfig{} 329 err = proto.Unmarshal(mspConfig.Config, fabricConfig) 330 Expect(err).NotTo(HaveOccurred()) 331 332 // Mutate it as we are asked 333 *fabricConfig = mutateMSP(*fabricConfig) 334 335 // Wrap it back into the config 336 mspConfig.Config = protoutil.MarshalOrPanic(fabricConfig) 337 rawMSPConfig.Value = protoutil.MarshalOrPanic(mspConfig) 338 339 UpdateOrdererConfig(network, orderer, channel, config, updatedConfig, peer, orderer) 340 }