github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/core/scc/cscc/configure.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package cscc chaincode configer provides functions to manage
    18  // configuration transactions as the network is being reconfigured. The
    19  // configuration transactions arrive from the ordering service to the committer
    20  // who calls this chaincode. The chaincode also provides peer configuration
    21  // services such as joining a chain or getting configuration data.
    22  package cscc
    23  
    24  import (
    25  	"errors"
    26  	"fmt"
    27  
    28  	"github.com/golang/protobuf/proto"
    29  	"github.com/hyperledger/fabric/common/config"
    30  	"github.com/hyperledger/fabric/common/flogging"
    31  	"github.com/hyperledger/fabric/common/policies"
    32  	"github.com/hyperledger/fabric/core/chaincode/shim"
    33  	"github.com/hyperledger/fabric/core/peer"
    34  	"github.com/hyperledger/fabric/core/policy"
    35  	"github.com/hyperledger/fabric/events/producer"
    36  	"github.com/hyperledger/fabric/msp/mgmt"
    37  	"github.com/hyperledger/fabric/protos/common"
    38  	pb "github.com/hyperledger/fabric/protos/peer"
    39  	"github.com/hyperledger/fabric/protos/utils"
    40  )
    41  
    42  // PeerConfiger implements the configuration handler for the peer. For every
    43  // configuration transaction coming in from the ordering service, the
    44  // committer calls this system chaincode to process the transaction.
    45  type PeerConfiger struct {
    46  	policyChecker policy.PolicyChecker
    47  }
    48  
    49  var cnflogger = flogging.MustGetLogger("cscc")
    50  
    51  // These are function names from Invoke first parameter
    52  const (
    53  	JoinChain      string = "JoinChain"
    54  	GetConfigBlock string = "GetConfigBlock"
    55  	GetChannels    string = "GetChannels"
    56  )
    57  
    58  // Init is called once per chain when the chain is created.
    59  // This allows the chaincode to initialize any variables on the ledger prior
    60  // to any transaction execution on the chain.
    61  func (e *PeerConfiger) Init(stub shim.ChaincodeStubInterface) pb.Response {
    62  	cnflogger.Info("Init CSCC")
    63  
    64  	// Init policy checker for access control
    65  	e.policyChecker = policy.NewPolicyChecker(
    66  		peer.NewChannelPolicyManagerGetter(),
    67  		mgmt.GetLocalMSP(),
    68  		mgmt.NewLocalMSPPrincipalGetter(),
    69  	)
    70  
    71  	return shim.Success(nil)
    72  }
    73  
    74  // Invoke is called for the following:
    75  // # to process joining a chain (called by app as a transaction proposal)
    76  // # to get the current configuration block (called by app)
    77  // # to update the configuration block (called by commmitter)
    78  // Peer calls this function with 2 arguments:
    79  // # args[0] is the function name, which must be JoinChain, GetConfigBlock or
    80  // UpdateConfigBlock
    81  // # args[1] is a configuration Block if args[0] is JoinChain or
    82  // UpdateConfigBlock; otherwise it is the chain id
    83  // TODO: Improve the scc interface to avoid marshal/unmarshal args
    84  func (e *PeerConfiger) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    85  	args := stub.GetArgs()
    86  
    87  	if len(args) < 1 {
    88  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
    89  	}
    90  
    91  	fname := string(args[0])
    92  
    93  	if fname != GetChannels && len(args) < 2 {
    94  		return shim.Error(fmt.Sprintf("Incorrect number of arguments, %d", len(args)))
    95  	}
    96  
    97  	cnflogger.Debugf("Invoke function: %s", fname)
    98  
    99  	// Handle ACL:
   100  	// 1. get the signed proposal
   101  	sp, err := stub.GetSignedProposal()
   102  	if err != nil {
   103  		return shim.Error(fmt.Sprintf("Failed getting signed proposal from stub: [%s]", err))
   104  	}
   105  
   106  	switch fname {
   107  	case JoinChain:
   108  		if args[1] == nil {
   109  			return shim.Error("Cannot join the channel <nil> configuration block provided")
   110  		}
   111  
   112  		block, err := utils.GetBlockFromBlockBytes(args[1])
   113  		if err != nil {
   114  			return shim.Error(fmt.Sprintf("Failed to reconstruct the genesis block, %s", err))
   115  		}
   116  
   117  		cid, err := utils.GetChainIDFromBlock(block)
   118  		if err != nil {
   119  			return shim.Error(fmt.Sprintf("\"JoinChain\" request failed to extract "+
   120  				"channel id from the block due to [%s]", err))
   121  		}
   122  
   123  		if err := validateConfigBlock(block); err != nil {
   124  			return shim.Error(fmt.Sprintf("\"JoinChain\" for chainID = %s failed because of validation "+
   125  				"of configuration block, because of %s", cid, err))
   126  		}
   127  
   128  		// 2. check local MSP Admins policy
   129  		if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
   130  			return shim.Error(fmt.Sprintf("\"JoinChain\" request failed authorization check "+
   131  				"for channel [%s]: [%s]", cid, err))
   132  		}
   133  
   134  		return joinChain(cid, block)
   135  	case GetConfigBlock:
   136  		// 2. check the channel reader policy
   137  		if err = e.policyChecker.CheckPolicy(string(args[1]), policies.ChannelApplicationReaders, sp); err != nil {
   138  			return shim.Error(fmt.Sprintf("\"GetConfigBlock\" request failed authorization check for channel [%s]: [%s]", args[1], err))
   139  		}
   140  		return getConfigBlock(args[1])
   141  	case GetChannels:
   142  		// 2. check local MSP Members policy
   143  		if err = e.policyChecker.CheckPolicyNoChannel(mgmt.Members, sp); err != nil {
   144  			return shim.Error(fmt.Sprintf("\"GetChannels\" request failed authorization check: [%s]", err))
   145  		}
   146  
   147  		return getChannels()
   148  
   149  	}
   150  	return shim.Error(fmt.Sprintf("Requested function %s not found.", fname))
   151  }
   152  
   153  // validateConfigBlock validate configuration block to see whenever it's contains valid config transaction
   154  func validateConfigBlock(block *common.Block) error {
   155  	envelopeConfig, err := utils.ExtractEnvelope(block, 0)
   156  	if err != nil {
   157  		return errors.New(fmt.Sprintf("Failed to %s", err))
   158  	}
   159  
   160  	configEnv := &common.ConfigEnvelope{}
   161  	_, err = utils.UnmarshalEnvelopeOfType(envelopeConfig, common.HeaderType_CONFIG, configEnv)
   162  	if err != nil {
   163  		return errors.New(fmt.Sprintf("Bad configuration envelope: %s", err))
   164  	}
   165  
   166  	if configEnv.Config == nil {
   167  		return errors.New("Nil config envelope Config")
   168  	}
   169  
   170  	if configEnv.Config.ChannelGroup == nil {
   171  		return errors.New("Nil channel group")
   172  	}
   173  
   174  	if configEnv.Config.ChannelGroup.Groups == nil {
   175  		return errors.New("No channel configuration groups are available")
   176  	}
   177  
   178  	_, exists := configEnv.Config.ChannelGroup.Groups[config.ApplicationGroupKey]
   179  	if !exists {
   180  		return errors.New(fmt.Sprintf("Invalid configuration block, missing %s "+
   181  			"configuration group", config.ApplicationGroupKey))
   182  	}
   183  
   184  	return nil
   185  }
   186  
   187  // joinChain will join the specified chain in the configuration block.
   188  // Since it is the first block, it is the genesis block containing configuration
   189  // for this chain, so we want to update the Chain object with this info
   190  func joinChain(chainID string, block *common.Block) pb.Response {
   191  	if err := peer.CreateChainFromBlock(block); err != nil {
   192  		return shim.Error(err.Error())
   193  	}
   194  
   195  	peer.InitChain(chainID)
   196  
   197  	if err := producer.SendProducerBlockEvent(block); err != nil {
   198  		cnflogger.Errorf("Error sending block event %s", err)
   199  	}
   200  
   201  	return shim.Success(nil)
   202  }
   203  
   204  // Return the current configuration block for the specified chainID. If the
   205  // peer doesn't belong to the chain, return error
   206  func getConfigBlock(chainID []byte) pb.Response {
   207  	if chainID == nil {
   208  		return shim.Error("ChainID must not be nil.")
   209  	}
   210  	block := peer.GetCurrConfigBlock(string(chainID))
   211  	if block == nil {
   212  		return shim.Error(fmt.Sprintf("Unknown chain ID, %s", string(chainID)))
   213  	}
   214  	blockBytes, err := utils.Marshal(block)
   215  	if err != nil {
   216  		return shim.Error(err.Error())
   217  	}
   218  
   219  	return shim.Success(blockBytes)
   220  }
   221  
   222  // getChannels returns information about all channels for this peer
   223  func getChannels() pb.Response {
   224  	channelInfoArray := peer.GetChannelsInfo()
   225  
   226  	// add array with info about all channels for this peer
   227  	cqr := &pb.ChannelQueryResponse{Channels: channelInfoArray}
   228  
   229  	cqrbytes, err := proto.Marshal(cqr)
   230  	if err != nil {
   231  		return shim.Error(err.Error())
   232  	}
   233  
   234  	return shim.Success(cqrbytes)
   235  }