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  }