github.com/binyushen/fabric@v2.1.1+incompatible/core/scc/cscc/configure.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  // Package cscc chaincode configer provides functions to manage
     8  // configuration transactions as the network is being reconfigured. The
     9  // configuration transactions arrive from the ordering service to the committer
    10  // who calls this chaincode. The chaincode also provides peer configuration
    11  // services such as joining a chain or getting configuration data.
    12  package cscc
    13  
    14  import (
    15  	"fmt"
    16  
    17  	"github.com/golang/protobuf/proto"
    18  	"github.com/hyperledger/fabric-chaincode-go/shim"
    19  	"github.com/hyperledger/fabric-protos-go/common"
    20  	pb "github.com/hyperledger/fabric-protos-go/peer"
    21  	"github.com/hyperledger/fabric/bccsp"
    22  	"github.com/hyperledger/fabric/common/channelconfig"
    23  	"github.com/hyperledger/fabric/common/config"
    24  	"github.com/hyperledger/fabric/common/flogging"
    25  	"github.com/hyperledger/fabric/core/aclmgmt"
    26  	"github.com/hyperledger/fabric/core/aclmgmt/resources"
    27  	"github.com/hyperledger/fabric/core/committer/txvalidator/v20/plugindispatcher"
    28  	"github.com/hyperledger/fabric/core/ledger"
    29  	"github.com/hyperledger/fabric/core/ledger/util"
    30  	"github.com/hyperledger/fabric/core/peer"
    31  	"github.com/hyperledger/fabric/core/policy"
    32  	"github.com/hyperledger/fabric/protoutil"
    33  	"github.com/pkg/errors"
    34  )
    35  
    36  // New creates a new instance of the CSCC.
    37  // Typically, only one will be created per peer instance.
    38  func New(
    39  	aclProvider aclmgmt.ACLProvider,
    40  	deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider,
    41  	lr plugindispatcher.LifecycleResources,
    42  	nr plugindispatcher.CollectionAndLifecycleResources,
    43  	policyChecker policy.PolicyChecker,
    44  	p *peer.Peer,
    45  	bccsp bccsp.BCCSP,
    46  ) *PeerConfiger {
    47  	return &PeerConfiger{
    48  		policyChecker:          policyChecker,
    49  		configMgr:              peer.NewConfigSupport(p),
    50  		aclProvider:            aclProvider,
    51  		deployedCCInfoProvider: deployedCCInfoProvider,
    52  		legacyLifecycle:        lr,
    53  		newLifecycle:           nr,
    54  		peer:                   p,
    55  		bccsp:                  bccsp,
    56  	}
    57  }
    58  
    59  func (e *PeerConfiger) Name() string              { return "cscc" }
    60  func (e *PeerConfiger) Chaincode() shim.Chaincode { return e }
    61  
    62  // PeerConfiger implements the configuration handler for the peer. For every
    63  // configuration transaction coming in from the ordering service, the
    64  // committer calls this system chaincode to process the transaction.
    65  type PeerConfiger struct {
    66  	policyChecker          policy.PolicyChecker
    67  	configMgr              config.Manager
    68  	aclProvider            aclmgmt.ACLProvider
    69  	deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider
    70  	legacyLifecycle        plugindispatcher.LifecycleResources
    71  	newLifecycle           plugindispatcher.CollectionAndLifecycleResources
    72  	peer                   *peer.Peer
    73  	bccsp                  bccsp.BCCSP
    74  }
    75  
    76  var cnflogger = flogging.MustGetLogger("cscc")
    77  
    78  // These are function names from Invoke first parameter
    79  const (
    80  	JoinChain      string = "JoinChain"
    81  	GetConfigBlock string = "GetConfigBlock"
    82  	GetChannels    string = "GetChannels"
    83  )
    84  
    85  // Init is mostly useless from an SCC perspective
    86  func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response {
    87  	cnflogger.Info("Init CSCC")
    88  	return shim.Success(nil)
    89  }
    90  
    91  // Invoke is called for the following:
    92  // # to process joining a chain (called by app as a transaction proposal)
    93  // # to get the current configuration block (called by app)
    94  // # to update the configuration block (called by committer)
    95  // Peer calls this function with 2 arguments:
    96  // # args[0] is the function name, which must be JoinChain, GetConfigBlock or
    97  // UpdateConfigBlock
    98  // # args[1] is a configuration Block if args[0] is JoinChain or
    99  // UpdateConfigBlock; otherwise it is the chain id
   100  // TODO: Improve the scc interface to avoid marshal/unmarshal args
   101  func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
   102  	args := stub.GetArgs()
   103  
   104  	if len(args) < 1 {
   105  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
   106  	}
   107  
   108  	fname := string(args[0])
   109  
   110  	if fname != GetChannels && len(args) < 2 {
   111  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
   112  	}
   113  
   114  	cnflogger.Debugf("Invoke function: %s", fname)
   115  
   116  	// Handle ACL:
   117  	// 1. get the signed proposal
   118  	sp, err := stub.GetSignedProposal()
   119  	if err != nil {
   120  		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
   121  	}
   122  
   123  	name, err := protoutil.InvokedChaincodeName(sp.ProposalBytes)
   124  	if err != nil {
   125  		return shim.Error(fmt.Sprintf("Failed to identify the called chaincode: %s", err))
   126  	}
   127  
   128  	if name != e.Name() {
   129  		return shim.Error(fmt.Sprintf("Rejecting invoke of CSCC from another chaincode, original invocation for '%s'", name))
   130  	}
   131  
   132  	return e.InvokeNoShim(args, sp)
   133  }
   134  
   135  func (e *PeerConfiger) InvokeNoShim(args [][]byte, sp *pb.SignedProposal) pb.Response {
   136  	var err error
   137  	fname := string(args[0])
   138  
   139  	switch fname {
   140  	case JoinChain:
   141  		if args[1] == nil {
   142  			return shim.Error("Cannot join the channel <nil> configuration block provided")
   143  		}
   144  
   145  		block, err := protoutil.UnmarshalBlock(args[1])
   146  		if err != nil {
   147  			return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err))
   148  		}
   149  
   150  		cid, err := protoutil.GetChainIDFromBlock(block)
   151  		if err != nil {
   152  			return shim.Error(fmt.Sprintf("\"JoinChain\" request failed to extract "+
   153  				"channel id from the block due to [%s]", err))
   154  		}
   155  
   156  		// 1. check config block's format and capabilities requirement.
   157  		if err := validateConfigBlock(block, e.bccsp); err != nil {
   158  			return shim.Error(fmt.Sprintf("\"JoinChain\" for chainID = %s failed because of validation "+
   159  				"of configuration block, because of %s", cid, err))
   160  		}
   161  
   162  		// 2. check join policy.
   163  		if err = e.aclProvider.CheckACL(resources.Cscc_JoinChain, "", sp); err != nil {
   164  			return shim.Error(fmt.Sprintf("access denied for [%s][%s]: [%s]", fname, cid, err))
   165  		}
   166  
   167  		// Initialize txsFilter if it does not yet exist. We can do this safely since
   168  		// it's the genesis block anyway
   169  		txsFilter := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
   170  		if len(txsFilter) == 0 {
   171  			// add array of validation code hardcoded to valid
   172  			txsFilter = util.NewTxValidationFlagsSetValue(len(block.Data.Data), pb.TxValidationCode_VALID)
   173  			block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] = txsFilter
   174  		}
   175  
   176  		return e.joinChain(cid, block, e.deployedCCInfoProvider, e.legacyLifecycle, e.newLifecycle)
   177  	case GetConfigBlock:
   178  		// 2. check policy
   179  		if err = e.aclProvider.CheckACL(resources.Cscc_GetConfigBlock, string(args[1]), sp); err != nil {
   180  			return shim.Error(fmt.Sprintf("access denied for [%s][%s]: %s", fname, args[1], err))
   181  		}
   182  
   183  		return e.getConfigBlock(args[1])
   184  	case GetChannels:
   185  		// 2. check get channels policy
   186  		if err = e.aclProvider.CheckACL(resources.Cscc_GetChannels, "", sp); err != nil {
   187  			return shim.Error(fmt.Sprintf("access denied for [%s]: %s", fname, err))
   188  		}
   189  
   190  		return e.getChannels()
   191  
   192  	}
   193  	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
   194  }
   195  
   196  // validateConfigBlock validate configuration block to see whenever it's contains valid config transaction
   197  func validateConfigBlock(block *common.Block, bccsp bccsp.BCCSP) error {
   198  	envelopeConfig, err := protoutil.ExtractEnvelope(block, 0)
   199  	if err != nil {
   200  		return errors.Errorf("Failed to %s", err)
   201  	}
   202  
   203  	configEnv := &common.ConfigEnvelope{}
   204  	_, err = protoutil.UnmarshalEnvelopeOfType(envelopeConfig, common.HeaderType_CONFIG, configEnv)
   205  	if err != nil {
   206  		return errors.Errorf("Bad configuration envelope: %s", err)
   207  	}
   208  
   209  	if configEnv.Config == nil {
   210  		return errors.New("Nil config envelope Config")
   211  	}
   212  
   213  	if configEnv.Config.ChannelGroup == nil {
   214  		return errors.New("Nil channel group")
   215  	}
   216  
   217  	if configEnv.Config.ChannelGroup.Groups == nil {
   218  		return errors.New("No channel configuration groups are available")
   219  	}
   220  
   221  	_, exists := configEnv.Config.ChannelGroup.Groups[channelconfig.ApplicationGroupKey]
   222  	if !exists {
   223  		return errors.Errorf("Invalid configuration block, missing %s "+
   224  			"configuration group", channelconfig.ApplicationGroupKey)
   225  	}
   226  
   227  	// Check the capabilities requirement
   228  	if err = channelconfig.ValidateCapabilities(block, bccsp); err != nil {
   229  		return errors.Errorf("Failed capabilities check: [%s]", err)
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  // joinChain will join the specified chain in the configuration block.
   236  // Since it is the first block, it is the genesis block containing configuration
   237  // for this chain, so we want to update the Chain object with this info
   238  func (e *PeerConfiger) joinChain(
   239  	chainID string,
   240  	block *common.Block,
   241  	deployedCCInfoProvider ledger.DeployedChaincodeInfoProvider,
   242  	lr plugindispatcher.LifecycleResources,
   243  	nr plugindispatcher.CollectionAndLifecycleResources,
   244  ) pb.Response {
   245  	if err := e.peer.CreateChannel(chainID, block, deployedCCInfoProvider, lr, nr); err != nil {
   246  		return shim.Error(err.Error())
   247  	}
   248  
   249  	return shim.Success(nil)
   250  }
   251  
   252  // Return the current configuration block for the specified chainID. If the
   253  // peer doesn't belong to the chain, return error
   254  func (e *PeerConfiger) getConfigBlock(chainID []byte) pb.Response {
   255  	if chainID == nil {
   256  		return shim.Error("ChainID must not be nil.")
   257  	}
   258  
   259  	channel := e.peer.Channel(string(chainID))
   260  	if channel == nil {
   261  		return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
   262  	}
   263  	block, err := peer.ConfigBlockFromLedger(channel.Ledger())
   264  	if err != nil {
   265  		return shim.Error(err.Error())
   266  	}
   267  
   268  	blockBytes, err := protoutil.Marshal(block)
   269  	if err != nil {
   270  		return shim.Error(err.Error())
   271  	}
   272  
   273  	return shim.Success(blockBytes)
   274  }
   275  
   276  func (e *PeerConfiger) supportByType(chainID []byte, env *common.Envelope) (config.Config, error) {
   277  	payload := &common.Payload{}
   278  
   279  	if err := proto.Unmarshal(env.Payload, payload); err != nil {
   280  		return nil, errors.Errorf("failed unmarshaling payload: %v", err)
   281  	}
   282  
   283  	channelHdr := &common.ChannelHeader{}
   284  	if err := proto.Unmarshal(payload.Header.ChannelHeader, channelHdr); err != nil {
   285  		return nil, errors.Errorf("failed unmarshaling payload header: %v", err)
   286  	}
   287  
   288  	switch common.HeaderType(channelHdr.Type) {
   289  	case common.HeaderType_CONFIG_UPDATE:
   290  		return e.configMgr.GetChannelConfig(string(chainID)), nil
   291  	}
   292  	return nil, errors.Errorf("invalid payload header type: %d", channelHdr.Type)
   293  }
   294  
   295  // getChannels returns information about all channels for this peer
   296  func (e *PeerConfiger) getChannels() pb.Response {
   297  	channelInfoArray := e.peer.GetChannelsInfo()
   298  
   299  	// add array with info about all channels for this peer
   300  	cqr := &pb.ChannelQueryResponse{Channels: channelInfoArray}
   301  
   302  	cqrbytes, err := proto.Marshal(cqr)
   303  	if err != nil {
   304  		return shim.Error(err.Error())
   305  	}
   306  
   307  	return shim.Success(cqrbytes)
   308  }