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