github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/chaincode/lifecycle/deployedcc_infoprovider.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package lifecycle
     8  
     9  import (
    10  	"fmt"
    11  	"regexp"
    12  	"strings"
    13  
    14  	cb "github.com/hyperledger/fabric-protos-go/common"
    15  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    16  	pb "github.com/hyperledger/fabric-protos-go/peer"
    17  	"github.com/hyperledger/fabric/common/cauthdsl"
    18  	"github.com/hyperledger/fabric/common/util"
    19  	validationState "github.com/hyperledger/fabric/core/handlers/validation/api/state"
    20  	"github.com/hyperledger/fabric/core/ledger"
    21  	"github.com/hyperledger/fabric/core/peer"
    22  	"github.com/hyperledger/fabric/protoutil"
    23  
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  //go:generate counterfeiter -o mock/legacy_ccinfo.go --fake-name LegacyDeployedCCInfoProvider . LegacyDeployedCCInfoProvider
    28  type LegacyDeployedCCInfoProvider interface {
    29  	ledger.DeployedChaincodeInfoProvider
    30  }
    31  
    32  const (
    33  	LifecycleEndorsementPolicyRef = "/Channel/Application/LifecycleEndorsement"
    34  )
    35  
    36  var (
    37  	// This is a channel which was created with a lifecycle endorsement policy
    38  	LifecycleDefaultEndorsementPolicyBytes = protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
    39  		Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{
    40  			ChannelConfigPolicyReference: LifecycleEndorsementPolicyRef,
    41  		},
    42  	})
    43  )
    44  
    45  type ValidatorCommitter struct {
    46  	CoreConfig                   *peer.Config
    47  	Resources                    *Resources
    48  	LegacyDeployedCCInfoProvider LegacyDeployedCCInfoProvider
    49  }
    50  
    51  // Namespaces returns the list of namespaces which are relevant to chaincode lifecycle
    52  func (vc *ValidatorCommitter) Namespaces() []string {
    53  	return append([]string{LifecycleNamespace}, vc.LegacyDeployedCCInfoProvider.Namespaces()...)
    54  }
    55  
    56  var SequenceMatcher = regexp.MustCompile("^" + NamespacesName + "/fields/([^/]+)/Sequence$")
    57  
    58  // UpdatedChaincodes returns the chaincodes that are getting updated by the supplied 'stateUpdates'
    59  func (vc *ValidatorCommitter) UpdatedChaincodes(stateUpdates map[string][]*kvrwset.KVWrite) ([]*ledger.ChaincodeLifecycleInfo, error) {
    60  	lifecycleInfo := []*ledger.ChaincodeLifecycleInfo{}
    61  
    62  	// If the lifecycle table was updated, report only modified chaincodes
    63  	lifecycleUpdates := stateUpdates[LifecycleNamespace]
    64  
    65  	for _, kvWrite := range lifecycleUpdates {
    66  		matches := SequenceMatcher.FindStringSubmatch(kvWrite.Key)
    67  		if len(matches) != 2 {
    68  			continue
    69  		}
    70  		// XXX Note, this may not be a chaincode namespace, handle this later
    71  		lifecycleInfo = append(lifecycleInfo, &ledger.ChaincodeLifecycleInfo{Name: matches[1]})
    72  	}
    73  
    74  	legacyUpdates, err := vc.LegacyDeployedCCInfoProvider.UpdatedChaincodes(stateUpdates)
    75  	if err != nil {
    76  		return nil, errors.WithMessage(err, "error invoking legacy deployed cc info provider")
    77  	}
    78  
    79  	return append(lifecycleInfo, legacyUpdates...), nil
    80  }
    81  
    82  func (vc *ValidatorCommitter) ChaincodeInfo(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*ledger.DeployedChaincodeInfo, error) {
    83  	exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
    84  		Namespace:           LifecycleNamespace,
    85  		SimpleQueryExecutor: qe,
    86  	})
    87  	if err != nil {
    88  		return nil, errors.WithMessage(err, "could not get info about chaincode")
    89  	}
    90  
    91  	if !exists {
    92  		return vc.LegacyDeployedCCInfoProvider.ChaincodeInfo(channelName, chaincodeName, qe)
    93  	}
    94  
    95  	return &ledger.DeployedChaincodeInfo{
    96  		Name:                        chaincodeName,
    97  		Version:                     definedChaincode.EndorsementInfo.Version,
    98  		Hash:                        util.ComputeSHA256([]byte(chaincodeName + ":" + definedChaincode.EndorsementInfo.Version)),
    99  		ExplicitCollectionConfigPkg: definedChaincode.Collections,
   100  		IsLegacy:                    false,
   101  	}, nil
   102  }
   103  
   104  var ImplicitCollectionMatcher = regexp.MustCompile("^" + ImplicitCollectionNameForOrg("(.+)") + "$")
   105  
   106  // AllCollectionsConfigPkg implements function in interface ledger.DeployedChaincodeInfoProvider
   107  // this implementation returns a combined collection config pkg that contains both explicit and implicit collections
   108  func (vc *ValidatorCommitter) AllCollectionsConfigPkg(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) (*pb.CollectionConfigPackage, error) {
   109  	chaincodeInfo, err := vc.ChaincodeInfo(channelName, chaincodeName, qe)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	explicitCollectionConfigPkg := chaincodeInfo.ExplicitCollectionConfigPkg
   114  
   115  	if chaincodeInfo.IsLegacy {
   116  		return explicitCollectionConfigPkg, nil
   117  	}
   118  
   119  	implicitCollections, err := vc.ImplicitCollections(channelName, chaincodeName, qe)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	var combinedColls []*pb.CollectionConfig
   125  	if explicitCollectionConfigPkg != nil {
   126  		combinedColls = append(combinedColls, explicitCollectionConfigPkg.Config...)
   127  	}
   128  	for _, implicitColl := range implicitCollections {
   129  		c := &pb.CollectionConfig{}
   130  		c.Payload = &pb.CollectionConfig_StaticCollectionConfig{StaticCollectionConfig: implicitColl}
   131  		combinedColls = append(combinedColls, c)
   132  	}
   133  	return &pb.CollectionConfigPackage{
   134  		Config: combinedColls,
   135  	}, nil
   136  }
   137  
   138  // CollectionInfo implements function in interface ledger.DeployedChaincodeInfoProvider, it returns config for
   139  // both static and implicit collections.
   140  func (vc *ValidatorCommitter) CollectionInfo(channelName, chaincodeName, collectionName string, qe ledger.SimpleQueryExecutor) (*pb.StaticCollectionConfig, error) {
   141  	exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
   142  		Namespace:           LifecycleNamespace,
   143  		SimpleQueryExecutor: qe,
   144  	})
   145  	if err != nil {
   146  		return nil, errors.WithMessage(err, "could not get chaincode")
   147  	}
   148  
   149  	if !exists {
   150  		return vc.LegacyDeployedCCInfoProvider.CollectionInfo(channelName, chaincodeName, collectionName, qe)
   151  	}
   152  
   153  	matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName)
   154  	if len(matches) == 2 {
   155  		return vc.GenerateImplicitCollectionForOrg(matches[1]), nil
   156  	}
   157  
   158  	if definedChaincode.Collections != nil {
   159  		for _, conf := range definedChaincode.Collections.Config {
   160  			staticCollConfig := conf.GetStaticCollectionConfig()
   161  			if staticCollConfig != nil && staticCollConfig.Name == collectionName {
   162  				return staticCollConfig, nil
   163  			}
   164  		}
   165  	}
   166  	return nil, nil
   167  }
   168  
   169  // ImplicitCollections implements function in interface ledger.DeployedChaincodeInfoProvider.  It returns
   170  //a slice that contains one proto msg for each of the implicit collections
   171  func (vc *ValidatorCommitter) ImplicitCollections(channelName, chaincodeName string, qe ledger.SimpleQueryExecutor) ([]*pb.StaticCollectionConfig, error) {
   172  	exists, _, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
   173  		Namespace:           LifecycleNamespace,
   174  		SimpleQueryExecutor: qe,
   175  	})
   176  	if err != nil {
   177  		return nil, errors.WithMessage(err, "could not get info about chaincode")
   178  	}
   179  
   180  	if !exists {
   181  		// Implicit collections are a v2.0 lifecycle concept, if the chaincode is not in the new lifecycle, return nothing
   182  		return nil, nil
   183  	}
   184  
   185  	return vc.ChaincodeImplicitCollections(channelName)
   186  }
   187  
   188  // ChaincodeImplicitCollections assumes the chaincode exists in the new lifecycle and returns the implicit collections
   189  func (vc *ValidatorCommitter) ChaincodeImplicitCollections(channelName string) ([]*pb.StaticCollectionConfig, error) {
   190  	channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelName)
   191  	if channelConfig == nil {
   192  		return nil, errors.Errorf("could not get channelconfig for channel %s", channelName)
   193  	}
   194  	ac, ok := channelConfig.ApplicationConfig()
   195  	if !ok {
   196  		return nil, errors.Errorf("could not get application config for channel %s", channelName)
   197  	}
   198  
   199  	orgs := ac.Organizations()
   200  	implicitCollections := make([]*pb.StaticCollectionConfig, 0, len(orgs))
   201  	for _, org := range orgs {
   202  		implicitCollections = append(implicitCollections, vc.GenerateImplicitCollectionForOrg(org.MSPID()))
   203  	}
   204  
   205  	return implicitCollections, nil
   206  }
   207  
   208  // GenerateImplicitCollectionForOrg generates implicit collection for the org
   209  func (vc *ValidatorCommitter) GenerateImplicitCollectionForOrg(mspid string) *pb.StaticCollectionConfig {
   210  	// set collection MaximumPeerCount to 0 when its mspid does not match the local mspid
   211  	// set collection MaximumPeerCount to 1 when its mspid matches the local mspid (i.e., peer's own org)
   212  	maxPeerCount := int32(0)
   213  	if mspid == vc.CoreConfig.LocalMSPID {
   214  		maxPeerCount = 1
   215  	}
   216  	return &pb.StaticCollectionConfig{
   217  		Name: ImplicitCollectionNameForOrg(mspid),
   218  		MemberOrgsPolicy: &pb.CollectionPolicyConfig{
   219  			Payload: &pb.CollectionPolicyConfig_SignaturePolicy{
   220  				SignaturePolicy: cauthdsl.SignedByMspMember(mspid),
   221  			},
   222  		},
   223  		RequiredPeerCount: 0,
   224  		MaximumPeerCount:  maxPeerCount,
   225  	}
   226  }
   227  
   228  func ImplicitCollectionNameForOrg(mspid string) string {
   229  	return fmt.Sprintf("_implicit_org_%s", mspid)
   230  }
   231  
   232  func OrgFromImplicitCollectionName(name string) string {
   233  	return strings.TrimPrefix(name, "_implicit_org_")
   234  }
   235  
   236  func (vc *ValidatorCommitter) ImplicitCollectionEndorsementPolicyAsBytes(channelID, orgMSPID string) (policy []byte, unexpectedErr, validationErr error) {
   237  	channelConfig := vc.Resources.ChannelConfigSource.GetStableChannelConfig(channelID)
   238  	if channelConfig == nil {
   239  		return nil, errors.Errorf("could not get channel config for channel '%s'", channelID), nil
   240  	}
   241  
   242  	ac, ok := channelConfig.ApplicationConfig()
   243  	if !ok {
   244  		return nil, errors.Errorf("could not get application config for channel '%s'", channelID), nil
   245  	}
   246  
   247  	matchedOrgName := ""
   248  	for orgName, org := range ac.Organizations() {
   249  		if org.MSPID() == orgMSPID {
   250  			matchedOrgName = orgName
   251  			break
   252  		}
   253  	}
   254  
   255  	if matchedOrgName == "" {
   256  		return nil, nil, errors.Errorf("no org found in channel with MSPID '%s'", orgMSPID)
   257  	}
   258  
   259  	policyName := fmt.Sprintf("/Channel/Application/%s/Endorsement", matchedOrgName)
   260  	if _, ok := channelConfig.PolicyManager().GetPolicy(policyName); ok {
   261  		return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
   262  			Type: &cb.ApplicationPolicy_ChannelConfigPolicyReference{
   263  				ChannelConfigPolicyReference: policyName,
   264  			},
   265  		}), nil, nil
   266  	}
   267  
   268  	// This was a channel which was upgraded or did not define an org level endorsement policy, use a default
   269  	// of "any member of the org"
   270  
   271  	return protoutil.MarshalOrPanic(&cb.ApplicationPolicy{
   272  		Type: &cb.ApplicationPolicy_SignaturePolicy{
   273  			SignaturePolicy: cauthdsl.SignedByAnyMember([]string{orgMSPID}),
   274  		},
   275  	}), nil, nil
   276  }
   277  
   278  // ValidationInfo returns the name and arguments of the validation plugin for the supplied
   279  // chaincode. The function returns two types of errors, unexpected errors and validation
   280  // errors. The reason for this is that this function is called from the validation code,
   281  // which needs to differentiate the two types of error to halt processing on the channel
   282  // if the unexpected error is not nil and mark the transaction as invalid if the validation
   283  // error is not nil.
   284  func (vc *ValidatorCommitter) ValidationInfo(channelID, chaincodeName string, qe ledger.SimpleQueryExecutor) (plugin string, args []byte, unexpectedErr error, validationErr error) {
   285  	// TODO, this is a bit of an overkill check, and will need to be scaled back for non-chaincode type namespaces
   286  	exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &SimpleQueryExecutorShim{
   287  		Namespace:           LifecycleNamespace,
   288  		SimpleQueryExecutor: qe,
   289  	})
   290  	if err != nil {
   291  		return "", nil, errors.WithMessage(err, "could not get chaincode"), nil
   292  	}
   293  
   294  	if !exists {
   295  		// TODO, this is inconsistent with how the legacy lifecycle reports
   296  		// that a missing chaincode is a validation error.  But, for now
   297  		// this is required to make the passthrough work.
   298  		return "", nil, nil, nil
   299  	}
   300  
   301  	if chaincodeName == LifecycleNamespace {
   302  		b, err := vc.Resources.LifecycleEndorsementPolicyAsBytes(channelID)
   303  		if err != nil {
   304  			return "", nil, errors.WithMessage(err, "unexpected failure to create lifecycle endorsement policy"), nil
   305  		}
   306  		return "vscc", b, nil, nil
   307  	}
   308  
   309  	return definedChaincode.ValidationInfo.ValidationPlugin, definedChaincode.ValidationInfo.ValidationParameter, nil, nil
   310  }
   311  
   312  // CollectionValidationInfo returns information about collections to the validation component
   313  func (vc *ValidatorCommitter) CollectionValidationInfo(channelID, chaincodeName, collectionName string, state validationState.State) (args []byte, unexpectedErr, validationErr error) {
   314  	exists, definedChaincode, err := vc.Resources.ChaincodeDefinitionIfDefined(chaincodeName, &ValidatorStateShim{
   315  		Namespace:      LifecycleNamespace,
   316  		ValidatorState: state,
   317  	})
   318  	if err != nil {
   319  		return nil, errors.WithMessage(err, "could not get chaincode"), nil
   320  	}
   321  
   322  	if !exists {
   323  		// TODO, this is inconsistent with how the legacy lifecycle reports
   324  		// that a missing chaincode is a validation error.  But, for now
   325  		// this is required to make the passthrough work.
   326  		return nil, nil, nil
   327  	}
   328  
   329  	matches := ImplicitCollectionMatcher.FindStringSubmatch(collectionName)
   330  	if len(matches) == 2 {
   331  		return vc.ImplicitCollectionEndorsementPolicyAsBytes(channelID, matches[1])
   332  	}
   333  
   334  	if definedChaincode.Collections != nil {
   335  		for _, conf := range definedChaincode.Collections.Config {
   336  			staticCollConfig := conf.GetStaticCollectionConfig()
   337  			if staticCollConfig != nil && staticCollConfig.Name == collectionName {
   338  				if staticCollConfig.EndorsementPolicy != nil {
   339  					return protoutil.MarshalOrPanic(staticCollConfig.EndorsementPolicy), nil, nil
   340  				}
   341  				// default to chaincode endorsement policy
   342  				return definedChaincode.ValidationInfo.ValidationParameter, nil, nil
   343  			}
   344  		}
   345  	}
   346  
   347  	return nil, nil, errors.Errorf("no such collection '%s'", collectionName)
   348  }