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  }