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  }