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