github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/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/inklabsfoundation/inkchain/common/flogging"
    32  	"github.com/inklabsfoundation/inkchain/core/ledger"
    33  	pb "github.com/inklabsfoundation/inkchain/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  
   174  // ccInfoFSStorageMgr is the storage manager used either by the cache or if the
   175  // cache is bypassed
   176  var ccInfoFSProvider = &CCInfoFSImpl{}
   177  
   178  // ccInfoCache is the cache instance itself
   179  var ccInfoCache = NewCCInfoCache(ccInfoFSProvider)
   180  
   181  // ccInfoCacheEnabled keeps track of whether the cache is enable
   182  // (it is disabled by default)
   183  var ccInfoCacheEnabled bool
   184  
   185  // EnableCCInfoCache can be called to enable the cache
   186  func EnableCCInfoCache() {
   187  	ccInfoCacheEnabled = true
   188  }
   189  
   190  // GetChaincodeFromFS retrieves chaincode information from the file system
   191  func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
   192  	return ccInfoFSProvider.GetChaincode(ccname, ccversion)
   193  }
   194  
   195  // PutChaincodeIntoFS puts chaincode information in the file system (and
   196  // also in the cache to prime it) if the cache is enabled, or directly
   197  // from the file system otherwise
   198  func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
   199  	_, err := ccInfoFSProvider.PutChaincode(depSpec)
   200  	return err
   201  }
   202  
   203  // GetChaincodeData gets chaincode data from cache if there's one
   204  func GetChaincodeData(ccname string, ccversion string) (*ChaincodeData, error) {
   205  	if ccInfoCacheEnabled {
   206  		ccproviderLogger.Debugf("Getting chaincode data for <%s, %s> from cache", ccname, ccversion)
   207  		return ccInfoCache.GetChaincodeData(ccname, ccversion)
   208  	}
   209  	if ccpack, err := ccInfoFSProvider.GetChaincode(ccname, ccversion); err != nil {
   210  		return nil, err
   211  	} else {
   212  		ccproviderLogger.Infof("Putting chaincode data for <%s, %s> into cache", ccname, ccversion)
   213  		return ccpack.GetChaincodeData(), nil
   214  	}
   215  }
   216  
   217  func CheckInsantiationPolicy(name, version string, cdLedger *ChaincodeData) error {
   218  	ccdata, err := GetChaincodeData(name, version)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	// we have the info from the fs, check that the policy
   224  	// matches the one on the file system if one was specified;
   225  	// this check is required because the admin of this peer
   226  	// might have specified instantiation policies for their
   227  	// chaincode, for example to make sure that the chaincode
   228  	// is only instantiated on certain channels; a malicious
   229  	// peer on the other hand might have created a deploy
   230  	// transaction that attempts to bypass the instantiation
   231  	// policy. This check is there to ensure that this will not
   232  	// happen, i.e. that the peer will refuse to invoke the
   233  	// chaincode under these conditions. More info on
   234  	if ccdata.InstantiationPolicy != nil {
   235  		if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) {
   236  			return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version)
   237  		}
   238  	}
   239  
   240  	return nil
   241  }
   242  
   243  // GetCCPackage tries each known package implementation one by one
   244  // till the right package is found
   245  func GetCCPackage(buf []byte) (CCPackage, error) {
   246  	//try raw CDS
   247  	cccdspack := &CDSPackage{}
   248  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   249  		//try signed CDS
   250  		ccscdspack := &SignedCDSPackage{}
   251  		if _, err := ccscdspack.InitFromBuffer(buf); err != nil {
   252  			return nil, err
   253  		}
   254  		return ccscdspack, nil
   255  	}
   256  	return cccdspack, nil
   257  }
   258  
   259  // GetInstalledChaincodes returns a map whose key is the chaincode id and
   260  // value is the ChaincodeDeploymentSpec struct for that chaincodes that have
   261  // been installed (but not necessarily instantiated) on the peer by searching
   262  // the chaincode install path
   263  func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
   264  	files, err := ioutil.ReadDir(chaincodeInstallPath)
   265  	if err != nil {
   266  		return nil, err
   267  	}
   268  
   269  	// array to store info for all chaincode entries from LSCC
   270  	var ccInfoArray []*pb.ChaincodeInfo
   271  
   272  	for _, file := range files {
   273  		// split at first period as chaincode versions can contain periods while
   274  		// chaincode names cannot
   275  		fileNameArray := strings.SplitN(file.Name(), ".", 2)
   276  
   277  		// check that length is 2 as expected, otherwise skip to next cc file
   278  		if len(fileNameArray) == 2 {
   279  			ccname := fileNameArray[0]
   280  			ccversion := fileNameArray[1]
   281  			ccpack, err := GetChaincodeFromFS(ccname, ccversion)
   282  			if err != nil {
   283  				// either chaincode on filesystem has been tampered with or
   284  				// a non-chaincode file has been found in the chaincodes directory
   285  				ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
   286  				continue
   287  			}
   288  
   289  			cdsfs := ccpack.GetDepSpec()
   290  
   291  			name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
   292  			version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
   293  			if name != ccname || version != ccversion {
   294  				// chaincode name/version in the chaincode file name has been modified
   295  				// by an external entity
   296  				ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
   297  				continue
   298  			}
   299  
   300  			path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
   301  			// since this is just an installed chaincode these should be blank
   302  			input, escc, vscc := "", "", ""
   303  
   304  			ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}
   305  
   306  			// add this specific chaincode's metadata to the array of all chaincodes
   307  			ccInfoArray = append(ccInfoArray, ccInfo)
   308  		}
   309  	}
   310  	// add array with info about all instantiated chaincodes to the query
   311  	// response proto
   312  	cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
   313  
   314  	return cqr, nil
   315  }
   316  
   317  //CCContext pass this around instead of string of args
   318  type CCContext struct {
   319  	//ChainID chain id
   320  	ChainID string
   321  
   322  	//Name chaincode name
   323  	Name string
   324  
   325  	//Version used to construct the chaincode image and register
   326  	Version string
   327  
   328  	//TxID is the transaction id for the proposal (if any)
   329  	TxID string
   330  
   331  	//Syscc is this a system chaincode
   332  	Syscc bool
   333  
   334  	//SignedProposal for this invoke (if any)
   335  	//this is kept here for access control and in case we need to pass something
   336  	//from this to the chaincode
   337  	SignedProposal *pb.SignedProposal
   338  
   339  	//Proposal for this invoke (if any)
   340  	//this is kept here just in case we need to pass something
   341  	//from this to the chaincode
   342  	Proposal *pb.Proposal
   343  
   344  	//this is not set but computed (note that this is not exported. use GetCanonicalName)
   345  	canonicalName string
   346  }
   347  
   348  //NewCCContext just construct a new struct with whatever args
   349  func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext {
   350  	//version CANNOT be empty. The chaincode namespace has to use version and chain name.
   351  	//All system chaincodes share the same version given by utils.GetSysCCVersion. Note
   352  	//that neither Chain Name or Version are stored in a chaincodes state on the ledger
   353  	if version == "" {
   354  		panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop))
   355  	}
   356  
   357  	canName := name + ":" + version
   358  
   359  	cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName}
   360  
   361  	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)
   362  
   363  	return cccid
   364  }
   365  
   366  //GetCanonicalName returns the canonical name associated with the proposal context
   367  func (cccid *CCContext) GetCanonicalName() string {
   368  	if cccid.canonicalName == "" {
   369  		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))
   370  	}
   371  
   372  	return cccid.canonicalName
   373  }
   374  
   375  //-------- ChaincodeData is stored on the LSCC -------
   376  
   377  //ChaincodeData defines the datastructure for chaincodes to be serialized by proto
   378  //Type provides an additional check by directing to use a specific package after instantiation
   379  //Data is Type specifc (see CDSPackage and SignedCDSPackage)
   380  type ChaincodeData struct {
   381  	//Name of the chaincode
   382  	Name string `protobuf:"bytes,1,opt,name=name"`
   383  
   384  	//Version of the chaincode
   385  	Version string `protobuf:"bytes,2,opt,name=version"`
   386  
   387  	//Escc for the chaincode instance
   388  	Escc string `protobuf:"bytes,3,opt,name=escc"`
   389  
   390  	//Vscc for the chaincode instance
   391  	Vscc string `protobuf:"bytes,4,opt,name=vscc"`
   392  
   393  	//Policy endorsement policy for the chaincode instance
   394  	Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`
   395  
   396  	//Data data specific to the package
   397  	Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`
   398  
   399  	//Id of the chaincode that's the unique fingerprint for the CC
   400  	//This is not currently used anywhere but serves as a good
   401  	//eyecatcher
   402  	Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
   403  
   404  	//InstantiationPolicy for the chaincode
   405  	InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
   406  }
   407  
   408  //implement functions needed from proto.Message for proto's mar/unmarshal functions
   409  
   410  //Reset resets
   411  func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
   412  
   413  //String converts to string
   414  func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
   415  
   416  //ProtoMessage just exists to make proto happy
   417  func (*ChaincodeData) ProtoMessage() {}
   418  
   419  // ChaincodeProvider provides an abstraction layer that is
   420  // used for different packages to interact with code in the
   421  // chaincode package without importing it; more methods
   422  // should be added below if necessary
   423  type ChaincodeProvider interface {
   424  	// GetContext returns a ledger context
   425  	GetContext(ledger ledger.PeerLedger) (context.Context, error)
   426  	// GetCCContext returns an opaque chaincode context
   427  	GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
   428  	// GetCCValidationInfoFromLSCC returns the VSCC and the policy listed by LSCC for the supplied chaincode
   429  	GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
   430  	// ExecuteChaincode executes the chaincode given context and args
   431  	ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
   432  	// Execute executes the chaincode given context and spec (invocation or deploy)
   433  	Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
   434  	// ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload
   435  	ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
   436  	// Stop stops the chaincode given context and deployment spec
   437  	Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
   438  	// ReleaseContext releases the context returned previously by GetContext
   439  	ReleaseContext()
   440  }
   441  
   442  var ccFactory ChaincodeProviderFactory
   443  
   444  // ChaincodeProviderFactory defines a factory interface so
   445  // that the actual implementation can be injected
   446  type ChaincodeProviderFactory interface {
   447  	NewChaincodeProvider() ChaincodeProvider
   448  }
   449  
   450  // RegisterChaincodeProviderFactory is to be called once to set
   451  // the factory that will be used to obtain instances of ChaincodeProvider
   452  func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
   453  	ccFactory = ccfact
   454  }
   455  
   456  // GetChaincodeProvider returns instances of ChaincodeProvider;
   457  // the actual implementation is controlled by the factory that
   458  // is registered via RegisterChaincodeProviderFactory
   459  func GetChaincodeProvider() ChaincodeProvider {
   460  	if ccFactory == nil {
   461  		panic("The factory must be set first via RegisterChaincodeProviderFactory")
   462  	}
   463  	return ccFactory.NewChaincodeProvider()
   464  }