github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/core/common/ccprovider/ccprovider.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 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 ccprovider
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  
    27  	"github.com/golang/protobuf/proto"
    28  
    29  	"bytes"
    30  
    31  	"github.com/hyperledger/fabric/common/flogging"
    32  	"github.com/hyperledger/fabric/core/ledger"
    33  	pb "github.com/hyperledger/fabric/protos/peer"
    34  )
    35  
    36  var ccproviderLogger = flogging.MustGetLogger("ccprovider")
    37  
    38  var chaincodeInstallPath string
    39  
    40  //CCPackage encapsulates a chaincode package which can be
    41  //    raw ChaincodeDeploymentSpec
    42  //    SignedChaincodeDeploymentSpec
    43  // Attempt to keep the interface at a level with minimal
    44  // interface for possible generalization.
    45  type CCPackage interface {
    46  	//InitFromBuffer initialize the package from bytes
    47  	InitFromBuffer(buf []byte) (*ChaincodeData, error)
    48  
    49  	// InitFromFS gets the chaincode from the filesystem (includes the raw bytes too)
    50  	InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)
    51  
    52  	// PutChaincodeToFS writes the chaincode to the filesystem
    53  	PutChaincodeToFS() error
    54  
    55  	// GetDepSpec gets the ChaincodeDeploymentSpec from the package
    56  	GetDepSpec() *pb.ChaincodeDeploymentSpec
    57  
    58  	// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
    59  	GetDepSpecBytes() []byte
    60  
    61  	// ValidateCC validates and returns the chaincode deployment spec corresponding to
    62  	// ChaincodeData. The validation is based on the metadata from ChaincodeData
    63  	// One use of this method is to validate the chaincode before launching
    64  	ValidateCC(ccdata *ChaincodeData) error
    65  
    66  	// GetPackageObject gets the object as a proto.Message
    67  	GetPackageObject() proto.Message
    68  
    69  	// GetChaincodeData gets the ChaincodeData
    70  	GetChaincodeData() *ChaincodeData
    71  
    72  	// GetId gets the fingerprint of the chaincode based on package computation
    73  	GetId() []byte
    74  }
    75  
    76  //SetChaincodesPath sets the chaincode path for this peer
    77  func SetChaincodesPath(path string) {
    78  	if s, err := os.Stat(path); err != nil {
    79  		if os.IsNotExist(err) {
    80  			if err := os.Mkdir(path, 0755); err != nil {
    81  				panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
    82  			}
    83  		} else {
    84  			panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
    85  		}
    86  	} else if !s.IsDir() {
    87  		panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
    88  	}
    89  
    90  	chaincodeInstallPath = path
    91  }
    92  
    93  //GetChaincodePackage returns the chaincode package from the file system
    94  func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {
    95  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
    96  	var ccbytes []byte
    97  	var err error
    98  	if ccbytes, err = ioutil.ReadFile(path); err != nil {
    99  		return nil, err
   100  	}
   101  	return ccbytes, nil
   102  }
   103  
   104  //ChaincodePackageExists returns whether the chaincode package exists in the file system
   105  func ChaincodePackageExists(ccname string, ccversion string) (bool, error) {
   106  	path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion)
   107  	_, err := os.Stat(path)
   108  	if err == nil {
   109  		// chaincodepackage already exists
   110  		return true, nil
   111  	}
   112  	return false, err
   113  }
   114  
   115  type CCCacheSupport interface {
   116  	//GetChaincode is needed by the cache to get chaincode data
   117  	GetChaincode(ccname string, ccversion string) (CCPackage, error)
   118  }
   119  
   120  // CCInfoFSImpl provides the implementation for CC on the FS and the access to it
   121  // It implements CCCacheSupport
   122  type CCInfoFSImpl struct{}
   123  
   124  // GetChaincodeFromFS this is a wrapper for hiding package implementation.
   125  func (*CCInfoFSImpl) GetChaincode(ccname string, ccversion string) (CCPackage, error) {
   126  	//try raw CDS
   127  	cccdspack := &CDSPackage{}
   128  	_, _, err := cccdspack.InitFromFS(ccname, ccversion)
   129  	if err != nil {
   130  		//try signed CDS
   131  		ccscdspack := &SignedCDSPackage{}
   132  		_, _, err = ccscdspack.InitFromFS(ccname, ccversion)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		return ccscdspack, nil
   137  	}
   138  	return cccdspack, nil
   139  }
   140  
   141  // PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec
   142  //using CDSPackage. This is only used in UTs
   143  func (*CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) {
   144  	buf, err := proto.Marshal(depSpec)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  	cccdspack := &CDSPackage{}
   149  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   150  		return nil, err
   151  	}
   152  	err = cccdspack.PutChaincodeToFS()
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return cccdspack, nil
   158  }
   159  
   160  // The following lines create the cache of CCPackage data that sits
   161  // on top of the file system and avoids a trip to the file system
   162  // every time. The cache is disabled by default and only enabled
   163  // if EnableCCInfoCache is called. This is an unfortunate hack
   164  // required by some legacy tests that remove chaincode packages
   165  // from the file system as a means of simulating particular test
   166  // conditions. This way of testing is incompatible with the
   167  // immutable nature of chaincode packages that is assumed by hlf v1
   168  // and implemented by this cache. For this reason, tests are for now
   169  // allowed to run with the cache disabled (unless they enable it)
   170  // until a later time in which they are fixed. The peer process on
   171  // the other hand requires the benefits of this cache and therefore
   172  // enables it.
   173  // TODO: (post v1) enable cache by default as soon as https://jira.hyperledger.org/browse/FAB-3785 is completed
   174  
   175  // ccInfoFSStorageMgr is the storage manager used either by the cache or if the
   176  // cache is bypassed
   177  var ccInfoFSProvider = &CCInfoFSImpl{}
   178  
   179  // ccInfoCache is the cache instance itself
   180  var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
   181  
   182  // ccInfoCacheEnabled keeps track of whether the cache is enable
   183  // (it is disabled by default)
   184  var ccInfoCacheEnabled bool
   185  
   186  // EnableCCInfoCache can be called to enable the cache
   187  func EnableCCInfoCache() {
   188  	ccInfoCacheEnabled = true
   189  }
   190  
   191  // GetChaincodeFromFS retrieves chaincode information from the file system
   192  func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
   193  	return ccInfoFSProvider.GetChaincode(ccname, ccversion)
   194  }
   195  
   196  // PutChaincodeIntoFS puts chaincode information in the file system (and
   197  // also in the cache to prime it) if the cache is enabled, or directly
   198  // from the file system otherwise
   199  func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
   200  	_, err := ccInfoFSProvider.PutChaincode(depSpec)
   201  	return err
   202  }
   203  
   204  // GetChaincodeData gets chaincode data from cache if there's one
   205  func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
   206  	if ccInfoCacheEnabled {
   207  		ccproviderLogger.Debugf("Getting chaincode data for <%s, %s> from cache", ccname, ccversion)
   208  		return ccInfoCache.GetChaincodeData(ccname, ccversion)
   209  	}
   210  	if ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion); err != nil {
   211  		return nil, err
   212  	} else {
   213  		ccproviderLogger.Infof("Putting chaincode data for <%s, %s> into cache", ccname, ccversion)
   214  		return ccpack.GetChaincodeData(), nil
   215  	}
   216  }
   217  
   218  func CheckInsantiationPolicy(name, version string, cdLedger *ChaincodeData) error {
   219  	ccdata, err := GetChaincodeData(name, version)
   220  	if err != nil {
   221  		return err
   222  	}
   223  
   224  	// we have the info from the fs, check that the policy
   225  	// matches the one on the file system if one was specified;
   226  	// this check is required because the admin of this peer
   227  	// might have specified instantiation policies for their
   228  	// chaincode, for example to make sure that the chaincode
   229  	// is only instantiated on certain channels; a malicious
   230  	// peer on the other hand might have created a deploy
   231  	// transaction that attempts to bypass the instantiation
   232  	// policy. This check is there to ensure that this will not
   233  	// happen, i.e. that the peer will refuse to invoke the
   234  	// chaincode under these conditions. More info on
   235  	// https://jira.hyperledger.org/browse/FAB-3156
   236  	if ccdata.InstantiationPolicy != nil {
   237  		if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) {
   238  			return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version)
   239  		}
   240  	}
   241  
   242  	return nil
   243  }
   244  
   245  // GetCCPackage tries each known package implementation one by one
   246  // till the right package is found
   247  func GetCCPackage(buf []byte) (CCPackage, error) {
   248  	//try raw CDS
   249  	cccdspack := &CDSPackage{}
   250  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   251  		//try signed CDS
   252  		ccscdspack := &SignedCDSPackage{}
   253  		if _, err := ccscdspack.InitFromBuffer(buf); err != nil {
   254  			return nil, err
   255  		}
   256  		return ccscdspack, nil
   257  	}
   258  	return cccdspack, nil
   259  }
   260  
   261  // GetInstalledChaincodes returns a map whose key is the chaincode id and
   262  // value is the ChaincodeDeploymentSpec struct for that chaincodes that have
   263  // been installed (but not necessarily instantiated) on the peer by searching
   264  // the chaincode install path
   265  func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
   266  	files, err := ioutil.ReadDir(chaincodeInstallPath)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  
   271  	// array to store info for all chaincode entries from LSCC
   272  	var ccInfoArray []*pb.ChaincodeInfo
   273  
   274  	for _, file := range files {
   275  		// split at first period as chaincode versions can contain periods while
   276  		// chaincode names cannot
   277  		fileNameArray := strings.SplitN(file.Name(), ".", 2)
   278  
   279  		// check that length is 2 as expected, otherwise skip to next cc file
   280  		if len(fileNameArray) == 2 {
   281  			ccname := fileNameArray[0]
   282  			ccversion := fileNameArray[1]
   283  			ccpack, err := GetChaincodeFromFS(ccname, ccversion)
   284  			if err != nil {
   285  				// either chaincode on filesystem has been tampered with or
   286  				// a non-chaincode file has been found in the chaincodes directory
   287  				ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
   288  				continue
   289  			}
   290  
   291  			cdsfs := ccpack.GetDepSpec()
   292  
   293  			name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
   294  			version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
   295  			if name != ccname || version != ccversion {
   296  				// chaincode name/version in the chaincode file name has been modified
   297  				// by an external entity
   298  				ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
   299  				continue
   300  			}
   301  
   302  			path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
   303  			// since this is just an installed chaincode these should be blank
   304  			input, escc, vscc := "", "", ""
   305  
   306  			ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}
   307  
   308  			// add this specific chaincode's metadata to the array of all chaincodes
   309  			ccInfoArray = append(ccInfoArray, ccInfo)
   310  		}
   311  	}
   312  	// add array with info about all instantiated chaincodes to the query
   313  	// response proto
   314  	cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
   315  
   316  	return cqr, nil
   317  }
   318  
   319  //CCContext pass this around instead of string of args
   320  type CCContext struct {
   321  	//ChainID chain id
   322  	ChainID string
   323  
   324  	//Name chaincode name
   325  	Name string
   326  
   327  	//Version used to construct the chaincode image and register
   328  	Version string
   329  
   330  	//TxID is the transaction id for the proposal (if any)
   331  	TxID string
   332  
   333  	//Syscc is this a system chaincode
   334  	Syscc bool
   335  
   336  	//SignedProposal for this invoke (if any)
   337  	//this is kept here for access control and in case we need to pass something
   338  	//from this to the chaincode
   339  	SignedProposal *pb.SignedProposal
   340  
   341  	//Proposal for this invoke (if any)
   342  	//this is kept here just in case we need to pass something
   343  	//from this to the chaincode
   344  	Proposal *pb.Proposal
   345  
   346  	//this is not set but computed (note that this is not exported. use GetCanonicalName)
   347  	canonicalName string
   348  }
   349  
   350  //NewCCContext just construct a new struct with whatever args
   351  func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext {
   352  	//version CANNOT be empty. The chaincode namespace has to use version and chain name.
   353  	//All system chaincodes share the same version given by utils.GetSysCCVersion. Note
   354  	//that neither Chain Name or Version are stored in a chaincodes state on the ledger
   355  	if version == "" {
   356  		panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop))
   357  	}
   358  
   359  	canName := name + ":" + version
   360  
   361  	cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName}
   362  
   363  	ccproviderLogger.Debugf("NewCCCC (chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p,canname=%s", cid, name, version, txid, syscc, prop, cccid.canonicalName)
   364  
   365  	return cccid
   366  }
   367  
   368  //GetCanonicalName returns the canonical name associated with the proposal context
   369  func (cccid *CCContext) GetCanonicalName() string {
   370  	if cccid.canonicalName == "" {
   371  		panic(fmt.Sprintf("cccid not constructed using NewCCContext(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t)", cccid.ChainID, cccid.Name, cccid.Version, cccid.TxID, cccid.Syscc))
   372  	}
   373  
   374  	return cccid.canonicalName
   375  }
   376  
   377  //-------- ChaincodeData is stored on the LSCC -------
   378  
   379  //ChaincodeData defines the datastructure for chaincodes to be serialized by proto
   380  //Type provides an additional check by directing to use a specific package after instantiation
   381  //Data is Type specifc (see CDSPackage and SignedCDSPackage)
   382  type ChaincodeData struct {
   383  	//Name of the chaincode
   384  	Name string `protobuf:"bytes,1,opt,name=name"`
   385  
   386  	//Version of the chaincode
   387  	Version string `protobuf:"bytes,2,opt,name=version"`
   388  
   389  	//Escc for the chaincode instance
   390  	Escc string `protobuf:"bytes,3,opt,name=escc"`
   391  
   392  	//Vscc for the chaincode instance
   393  	Vscc string `protobuf:"bytes,4,opt,name=vscc"`
   394  
   395  	//Policy endorsement policy for the chaincode instance
   396  	Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`
   397  
   398  	//Data data specific to the package
   399  	Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`
   400  
   401  	//Id of the chaincode that's the unique fingerprint for the CC
   402  	//This is not currently used anywhere but serves as a good
   403  	//eyecatcher
   404  	Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
   405  
   406  	//InstantiationPolicy for the chaincode
   407  	InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
   408  }
   409  
   410  //implement functions needed from proto.Message for proto's mar/unmarshal functions
   411  
   412  //Reset resets
   413  func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
   414  
   415  //String converts to string
   416  func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
   417  
   418  //ProtoMessage just exists to make proto happy
   419  func (*ChaincodeData) ProtoMessage() {}
   420  
   421  // ChaincodeProvider provides an abstraction layer that is
   422  // used for different packages to interact with code in the
   423  // chaincode package without importing it; more methods
   424  // should be added below if necessary
   425  type ChaincodeProvider interface {
   426  	// GetContext returns a ledger context
   427  	GetContext(ledger ledger.PeerLedger) (context.Context, error)
   428  	// GetCCContext returns an opaque chaincode context
   429  	GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
   430  	// GetCCValidationInfoFromLSCC returns the VSCC and the policy listed by LSCC for the supplied chaincode
   431  	GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
   432  	// ExecuteChaincode executes the chaincode given context and args
   433  	ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
   434  	// Execute executes the chaincode given context and spec (invocation or deploy)
   435  	Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
   436  	// ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload
   437  	ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
   438  	// Stop stops the chaincode given context and deployment spec
   439  	Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
   440  	// ReleaseContext releases the context returned previously by GetContext
   441  	ReleaseContext()
   442  }
   443  
   444  var ccFactory ChaincodeProviderFactory
   445  
   446  // ChaincodeProviderFactory defines a factory interface so
   447  // that the actual implementation can be injected
   448  type ChaincodeProviderFactory interface {
   449  	NewChaincodeProvider() ChaincodeProvider
   450  }
   451  
   452  // RegisterChaincodeProviderFactory is to be called once to set
   453  // the factory that will be used to obtain instances of ChaincodeProvider
   454  func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
   455  	ccFactory = ccfact
   456  }
   457  
   458  // GetChaincodeProvider returns instances of ChaincodeProvider;
   459  // the actual implementation is controlled by the factory that
   460  // is registered via RegisterChaincodeProviderFactory
   461  func GetChaincodeProvider() ChaincodeProvider {
   462  	if ccFactory == nil {
   463  		panic("The factory must be set first via RegisterChaincodeProviderFactory")
   464  	}
   465  	return ccFactory.NewChaincodeProvider()
   466  }