github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/scc/cscc/configure.go (about)

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