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