github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/core/common/ccprovider/ccprovider.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ccprovider
     8  
     9  import (
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"unicode"
    16  
    17  	"github.com/golang/protobuf/proto"
    18  	pb "github.com/hyperledger/fabric-protos-go/peer"
    19  	"github.com/hyperledger/fabric/bccsp"
    20  	"github.com/hyperledger/fabric/bccsp/factory"
    21  	"github.com/hyperledger/fabric/common/chaincode"
    22  	"github.com/hyperledger/fabric/common/flogging"
    23  	"github.com/hyperledger/fabric/core/common/privdata"
    24  	"github.com/hyperledger/fabric/core/ledger"
    25  	"github.com/pkg/errors"
    26  )
    27  
    28  var ccproviderLogger = flogging.MustGetLogger("ccprovider")
    29  
    30  var chaincodeInstallPath string
    31  
    32  // CCPackage encapsulates a chaincode package which can be
    33  //    raw ChaincodeDeploymentSpec
    34  //    SignedChaincodeDeploymentSpec
    35  // Attempt to keep the interface at a level with minimal
    36  // interface for possible generalization.
    37  type CCPackage interface {
    38  	//InitFromBuffer initialize the package from bytes
    39  	InitFromBuffer(buf []byte) (*ChaincodeData, error)
    40  
    41  	// PutChaincodeToFS writes the chaincode to the filesystem
    42  	PutChaincodeToFS() error
    43  
    44  	// GetDepSpec gets the ChaincodeDeploymentSpec from the package
    45  	GetDepSpec() *pb.ChaincodeDeploymentSpec
    46  
    47  	// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
    48  	GetDepSpecBytes() []byte
    49  
    50  	// ValidateCC validates and returns the chaincode deployment spec corresponding to
    51  	// ChaincodeData. The validation is based on the metadata from ChaincodeData
    52  	// One use of this method is to validate the chaincode before launching
    53  	ValidateCC(ccdata *ChaincodeData) error
    54  
    55  	// GetPackageObject gets the object as a proto.Message
    56  	GetPackageObject() proto.Message
    57  
    58  	// GetChaincodeData gets the ChaincodeData
    59  	GetChaincodeData() *ChaincodeData
    60  
    61  	// GetId gets the fingerprint of the chaincode based on package computation
    62  	GetId() []byte
    63  }
    64  
    65  // SetChaincodesPath sets the chaincode path for this peer
    66  func SetChaincodesPath(path string) {
    67  	if s, err := os.Stat(path); err != nil {
    68  		if os.IsNotExist(err) {
    69  			if err := os.Mkdir(path, 0755); err != nil {
    70  				panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
    71  			}
    72  		} else {
    73  			panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
    74  		}
    75  	} else if !s.IsDir() {
    76  		panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
    77  	}
    78  
    79  	chaincodeInstallPath = path
    80  }
    81  
    82  // isPrintable is used by CDSPackage and SignedCDSPackage validation to
    83  // detect garbage strings in unmarshaled proto fields where printable
    84  // characters are expected.
    85  func isPrintable(name string) bool {
    86  	notASCII := func(r rune) bool {
    87  		return !unicode.IsPrint(r)
    88  	}
    89  	return strings.IndexFunc(name, notASCII) == -1
    90  }
    91  
    92  // GetChaincodePackage returns the chaincode package from the file system
    93  func GetChaincodePackageFromPath(ccNameVersion string, ccInstallPath string) ([]byte, error) {
    94  	path := fmt.Sprintf("%s/%s", ccInstallPath, strings.ReplaceAll(ccNameVersion, ":", "."))
    95  	var ccbytes []byte
    96  	var err error
    97  	if ccbytes, err = ioutil.ReadFile(path); err != nil {
    98  		return nil, err
    99  	}
   100  	return ccbytes, nil
   101  }
   102  
   103  // ChaincodePackageExists returns whether the chaincode package exists in the file system
   104  func ChaincodePackageExists(ccname string, ccversion string) (bool, error) {
   105  	path := filepath.Join(chaincodeInstallPath, ccname+"."+ccversion)
   106  	_, err := os.Stat(path)
   107  	if err == nil {
   108  		// chaincodepackage already exists
   109  		return true, nil
   110  	}
   111  	return false, err
   112  }
   113  
   114  type CCCacheSupport interface {
   115  	// GetChaincode is needed by the cache to get chaincode data
   116  	GetChaincode(ccNameVersion string) (CCPackage, error)
   117  }
   118  
   119  // CCInfoFSImpl provides the implementation for CC on the FS and the access to it
   120  // It implements CCCacheSupport
   121  type CCInfoFSImpl struct {
   122  	GetHasher GetHasher
   123  }
   124  
   125  // GetChaincodeFromFS this is a wrapper for hiding package implementation.
   126  // It calls GetChaincodeFromPath with the chaincodeInstallPath
   127  func (cifs *CCInfoFSImpl) GetChaincode(ccNameVersion string) (CCPackage, error) {
   128  	return cifs.GetChaincodeFromPath(ccNameVersion, chaincodeInstallPath)
   129  }
   130  
   131  func (cifs *CCInfoFSImpl) GetChaincodeCodePackage(ccNameVersion string) ([]byte, error) {
   132  	ccpack, err := cifs.GetChaincode(ccNameVersion)
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  	return ccpack.GetDepSpec().CodePackage, nil
   137  }
   138  
   139  func (cifs *CCInfoFSImpl) GetChaincodeDepSpec(ccNameVersion string) (*pb.ChaincodeDeploymentSpec, error) {
   140  	ccpack, err := cifs.GetChaincode(ccNameVersion)
   141  	if err != nil {
   142  		return nil, err
   143  	}
   144  	return ccpack.GetDepSpec(), nil
   145  }
   146  
   147  // GetChaincodeFromPath this is a wrapper for hiding package implementation.
   148  func (cifs *CCInfoFSImpl) GetChaincodeFromPath(ccNameVersion string, path string) (CCPackage, error) {
   149  	// try raw CDS
   150  	cccdspack := &CDSPackage{GetHasher: cifs.GetHasher}
   151  	_, _, err := cccdspack.InitFromPath(ccNameVersion, path)
   152  	if err != nil {
   153  		// try signed CDS
   154  		ccscdspack := &SignedCDSPackage{GetHasher: cifs.GetHasher}
   155  		_, _, err = ccscdspack.InitFromPath(ccNameVersion, path)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  		return ccscdspack, nil
   160  	}
   161  	return cccdspack, nil
   162  }
   163  
   164  // GetChaincodeInstallPath returns the path to the installed chaincodes
   165  func (*CCInfoFSImpl) GetChaincodeInstallPath() string {
   166  	return chaincodeInstallPath
   167  }
   168  
   169  // PutChaincode is a wrapper for putting raw ChaincodeDeploymentSpec
   170  //using CDSPackage. This is only used in UTs
   171  func (cifs *CCInfoFSImpl) PutChaincode(depSpec *pb.ChaincodeDeploymentSpec) (CCPackage, error) {
   172  	buf, err := proto.Marshal(depSpec)
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  	cccdspack := &CDSPackage{GetHasher: cifs.GetHasher}
   177  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   178  		return nil, err
   179  	}
   180  	err = cccdspack.PutChaincodeToFS()
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	return cccdspack, nil
   186  }
   187  
   188  // DirEnumerator enumerates directories
   189  type DirEnumerator func(string) ([]os.FileInfo, error)
   190  
   191  // ChaincodeExtractor extracts chaincode from a given path
   192  type ChaincodeExtractor func(ccNameVersion string, path string, getHasher GetHasher) (CCPackage, error)
   193  
   194  // ListInstalledChaincodes retrieves the installed chaincodes
   195  func (cifs *CCInfoFSImpl) ListInstalledChaincodes(dir string, ls DirEnumerator, ccFromPath ChaincodeExtractor) ([]chaincode.InstalledChaincode, error) {
   196  	var chaincodes []chaincode.InstalledChaincode
   197  	if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
   198  		return nil, nil
   199  	}
   200  	files, err := ls(dir)
   201  	if err != nil {
   202  		return nil, errors.Wrapf(err, "failed reading directory %s", dir)
   203  	}
   204  
   205  	for _, f := range files {
   206  		// Skip directories, we're only interested in normal files
   207  		if f.IsDir() {
   208  			continue
   209  		}
   210  		// A chaincode file name is of the type "name.version"
   211  		// We're only interested in the name.
   212  		// Skip files that don't adhere to the file naming convention of "A.B"
   213  		i := strings.Index(f.Name(), ".")
   214  		if i == -1 {
   215  			ccproviderLogger.Info("Skipping", f.Name(), "because of missing separator '.'")
   216  			continue
   217  		}
   218  		ccName := f.Name()[:i]      // Everything before the separator
   219  		ccVersion := f.Name()[i+1:] // Everything after the separator
   220  
   221  		ccPackage, err := ccFromPath(ccName+":"+ccVersion, dir, cifs.GetHasher)
   222  		if err != nil {
   223  			ccproviderLogger.Warning("Failed obtaining chaincode information about", ccName, ccVersion, ":", err)
   224  			return nil, errors.Wrapf(err, "failed obtaining information about %s, version %s", ccName, ccVersion)
   225  		}
   226  
   227  		chaincodes = append(chaincodes, chaincode.InstalledChaincode{
   228  			Name:    ccName,
   229  			Version: ccVersion,
   230  			Hash:    ccPackage.GetId(),
   231  		})
   232  	}
   233  	ccproviderLogger.Debug("Returning", chaincodes)
   234  	return chaincodes, nil
   235  }
   236  
   237  // ccInfoFSStorageMgr is the storage manager used either by the cache or if the
   238  // cache is bypassed
   239  var ccInfoFSProvider = &CCInfoFSImpl{GetHasher: factory.GetDefault()}
   240  
   241  // ccInfoCache is the cache instance itself
   242  var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
   243  
   244  // GetChaincodeFromFS retrieves chaincode information from the file system
   245  func GetChaincodeFromFS(ccNameVersion string) (CCPackage, error) {
   246  	return ccInfoFSProvider.GetChaincode(ccNameVersion)
   247  }
   248  
   249  // GetChaincodeData gets chaincode data from cache if there's one
   250  func GetChaincodeData(ccNameVersion string) (*ChaincodeData, error) {
   251  	ccproviderLogger.Debugf("Getting chaincode data for <%s> from cache", ccNameVersion)
   252  	return ccInfoCache.GetChaincodeData(ccNameVersion)
   253  }
   254  
   255  // GetCCPackage tries each known package implementation one by one
   256  // till the right package is found
   257  func GetCCPackage(buf []byte, bccsp bccsp.BCCSP) (CCPackage, error) {
   258  	// try raw CDS
   259  	cds := &CDSPackage{GetHasher: bccsp}
   260  	if ccdata, err := cds.InitFromBuffer(buf); err != nil {
   261  		cds = nil
   262  	} else {
   263  		err = cds.ValidateCC(ccdata)
   264  		if err != nil {
   265  			cds = nil
   266  		}
   267  	}
   268  
   269  	// try signed CDS
   270  	scds := &SignedCDSPackage{GetHasher: bccsp}
   271  	if ccdata, err := scds.InitFromBuffer(buf); err != nil {
   272  		scds = nil
   273  	} else {
   274  		err = scds.ValidateCC(ccdata)
   275  		if err != nil {
   276  			scds = nil
   277  		}
   278  	}
   279  
   280  	if cds != nil && scds != nil {
   281  		// Both were unmarshaled successfully, this is exactly why the approach of
   282  		// hoping proto fails for bad inputs is fatally flawed.
   283  		ccproviderLogger.Errorf("Could not determine chaincode package type, guessing SignedCDS")
   284  		return scds, nil
   285  	}
   286  
   287  	if cds != nil {
   288  		return cds, nil
   289  	}
   290  
   291  	if scds != nil {
   292  		return scds, nil
   293  	}
   294  
   295  	return nil, errors.New("could not unmarshal chaincode package to CDS or SignedCDS")
   296  }
   297  
   298  // GetInstalledChaincodes returns a map whose key is the chaincode id and
   299  // value is the ChaincodeDeploymentSpec struct for that chaincodes that have
   300  // been installed (but not necessarily instantiated) on the peer by searching
   301  // the chaincode install path
   302  func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
   303  	files, err := ioutil.ReadDir(chaincodeInstallPath)
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  
   308  	// array to store info for all chaincode entries from LSCC
   309  	var ccInfoArray []*pb.ChaincodeInfo
   310  
   311  	for _, file := range files {
   312  		// split at first period as chaincode versions can contain periods while
   313  		// chaincode names cannot
   314  		fileNameArray := strings.SplitN(file.Name(), ".", 2)
   315  
   316  		// check that length is 2 as expected, otherwise skip to next cc file
   317  		if len(fileNameArray) == 2 {
   318  			ccname := fileNameArray[0]
   319  			ccversion := fileNameArray[1]
   320  			ccpack, err := GetChaincodeFromFS(ccname + ":" + ccversion)
   321  			if err != nil {
   322  				// either chaincode on filesystem has been tampered with or
   323  				// _lifecycle chaincode files exist in the chaincodes directory.
   324  				continue
   325  			}
   326  
   327  			cdsfs := ccpack.GetDepSpec()
   328  
   329  			name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
   330  			version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
   331  			if name != ccname || version != ccversion {
   332  				// chaincode name/version in the chaincode file name has been modified
   333  				// by an external entity
   334  				ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
   335  				continue
   336  			}
   337  
   338  			path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
   339  			// since this is just an installed chaincode these should be blank
   340  			input, escc, vscc := "", "", ""
   341  
   342  			ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc, Id: ccpack.GetId()}
   343  
   344  			// add this specific chaincode's metadata to the array of all chaincodes
   345  			ccInfoArray = append(ccInfoArray, ccInfo)
   346  		}
   347  	}
   348  	// add array with info about all instantiated chaincodes to the query
   349  	// response proto
   350  	cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
   351  
   352  	return cqr, nil
   353  }
   354  
   355  //-------- ChaincodeData is stored on the LSCC -------
   356  
   357  // ChaincodeData defines the datastructure for chaincodes to be serialized by proto
   358  // Type provides an additional check by directing to use a specific package after instantiation
   359  // Data is Type specific (see CDSPackage and SignedCDSPackage)
   360  type ChaincodeData struct {
   361  	// Name of the chaincode
   362  	Name string `protobuf:"bytes,1,opt,name=name"`
   363  
   364  	// Version of the chaincode
   365  	Version string `protobuf:"bytes,2,opt,name=version"`
   366  
   367  	// Escc for the chaincode instance
   368  	Escc string `protobuf:"bytes,3,opt,name=escc"`
   369  
   370  	// Vscc for the chaincode instance
   371  	Vscc string `protobuf:"bytes,4,opt,name=vscc"`
   372  
   373  	// Policy endorsement policy for the chaincode instance
   374  	Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`
   375  
   376  	// Data data specific to the package
   377  	Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`
   378  
   379  	// Id of the chaincode that's the unique fingerprint for the CC This is not
   380  	// currently used anywhere but serves as a good eyecatcher
   381  	Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
   382  
   383  	// InstantiationPolicy for the chaincode
   384  	InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
   385  }
   386  
   387  // ChaincodeID is the name by which the chaincode will register itself.
   388  func (cd *ChaincodeData) ChaincodeID() string {
   389  	return cd.Name + ":" + cd.Version
   390  }
   391  
   392  // implement functions needed from proto.Message for proto's mar/unmarshal functions
   393  
   394  // Reset resets
   395  func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
   396  
   397  // String converts to string
   398  func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
   399  
   400  // ProtoMessage just exists to make proto happy
   401  func (*ChaincodeData) ProtoMessage() {}
   402  
   403  // TransactionParams are parameters which are tied to a particular transaction
   404  // and which are required for invoking chaincode.
   405  type TransactionParams struct {
   406  	TxID                 string
   407  	ChannelID            string
   408  	NamespaceID          string
   409  	SignedProp           *pb.SignedProposal
   410  	Proposal             *pb.Proposal
   411  	TXSimulator          ledger.TxSimulator
   412  	HistoryQueryExecutor ledger.HistoryQueryExecutor
   413  	CollectionStore      privdata.CollectionStore
   414  	IsInitTransaction    bool
   415  
   416  	// this is additional data passed to the chaincode
   417  	ProposalDecorations map[string][]byte
   418  }