github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/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  	"strings"
    25  
    26  	"github.com/golang/protobuf/proto"
    27  
    28  	"github.com/hyperledger/fabric/common/flogging"
    29  	"github.com/hyperledger/fabric/core/ledger"
    30  	pb "github.com/hyperledger/fabric/protos/peer"
    31  )
    32  
    33  var ccproviderLogger = flogging.MustGetLogger("ccprovider")
    34  
    35  var chaincodeInstallPath string
    36  
    37  //CCPackage encapsulates a chaincode package which can be
    38  //    raw ChaincodeDeploymentSpec
    39  //    SignedChaincodeDeploymentSpec
    40  // Attempt to keep the interface at a level with minimal
    41  // interface for possible generalization.
    42  type CCPackage interface {
    43  	//InitFromBuffer initialize the package from bytes
    44  	InitFromBuffer(buf []byte) (*ChaincodeData, error)
    45  
    46  	// InitFromFS gets the chaincode from the filesystem (includes the raw bytes too)
    47  	InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error)
    48  
    49  	// PutChaincodeToFS writes the chaincode to the filesystem
    50  	PutChaincodeToFS() error
    51  
    52  	// GetDepSpec gets the ChaincodeDeploymentSpec from the package
    53  	GetDepSpec() *pb.ChaincodeDeploymentSpec
    54  
    55  	// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
    56  	GetDepSpecBytes() []byte
    57  
    58  	// ValidateCC validates and returns the chaincode deployment spec corresponding to
    59  	// ChaincodeData. The validation is based on the metadata from ChaincodeData
    60  	// One use of this method is to validate the chaincode before launching
    61  	ValidateCC(ccdata *ChaincodeData) error
    62  
    63  	// GetPackageObject gets the object as a proto.Message
    64  	GetPackageObject() proto.Message
    65  
    66  	// GetChaincodeData gets the ChaincodeData
    67  	GetChaincodeData() *ChaincodeData
    68  
    69  	// GetId gets the fingerprint of the chaincode based on package computation
    70  	GetId() []byte
    71  }
    72  
    73  //SetChaincodesPath sets the chaincode path for this peer
    74  func SetChaincodesPath(path string) {
    75  	if s, err := os.Stat(path); err != nil {
    76  		if os.IsNotExist(err) {
    77  			if err := os.Mkdir(path, 0755); err != nil {
    78  				panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
    79  			}
    80  		} else {
    81  			panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
    82  		}
    83  	} else if !s.IsDir() {
    84  		panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
    85  	}
    86  
    87  	chaincodeInstallPath = path
    88  }
    89  
    90  //GetChaincodePackage returns the chaincode package from the file system
    91  func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {
    92  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
    93  	var ccbytes []byte
    94  	var err error
    95  	if ccbytes, err = ioutil.ReadFile(path); err != nil {
    96  		return nil, err
    97  	}
    98  	return ccbytes, nil
    99  }
   100  
   101  // GetChaincodeFromFS this is a wrapper for hiding package implementation.
   102  func GetChaincodeFromFS(ccname string, ccversion string) (CCPackage, error) {
   103  	//try raw CDS
   104  	cccdspack := &CDSPackage{}
   105  	_, _, err := cccdspack.InitFromFS(ccname, ccversion)
   106  	if err != nil {
   107  		//try signed CDS
   108  		ccscdspack := &SignedCDSPackage{}
   109  		_, _, err = ccscdspack.InitFromFS(ccname, ccversion)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  		return ccscdspack, nil
   114  	}
   115  	return cccdspack, nil
   116  }
   117  
   118  // PutChaincodeIntoFS is a wrapper for putting raw ChaincodeDeploymentSpec
   119  //using CDSPackage. This is only used in UTs
   120  func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
   121  	buf, err := proto.Marshal(depSpec)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	cccdspack := &CDSPackage{}
   126  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   127  		return err
   128  	}
   129  	return cccdspack.PutChaincodeToFS()
   130  }
   131  
   132  // GetCCPackage tries each known package implementation one by one
   133  // till the right package is found
   134  func GetCCPackage(buf []byte) (CCPackage, error) {
   135  	//try raw CDS
   136  	cccdspack := &CDSPackage{}
   137  	if _, err := cccdspack.InitFromBuffer(buf); err != nil {
   138  		//try signed CDS
   139  		ccscdspack := &SignedCDSPackage{}
   140  		if _, err := ccscdspack.InitFromBuffer(buf); err != nil {
   141  			return nil, err
   142  		}
   143  		return ccscdspack, nil
   144  	}
   145  	return cccdspack, nil
   146  }
   147  
   148  // GetInstalledChaincodes returns a map whose key is the chaincode id and
   149  // value is the ChaincodeDeploymentSpec struct for that chaincodes that have
   150  // been installed (but not necessarily instantiated) on the peer by searching
   151  // the chaincode install path
   152  func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
   153  	files, err := ioutil.ReadDir(chaincodeInstallPath)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	// array to store info for all chaincode entries from LSCC
   159  	var ccInfoArray []*pb.ChaincodeInfo
   160  
   161  	for _, file := range files {
   162  		// split at first period as chaincode versions can contain periods while
   163  		// chaincode names cannot
   164  		fileNameArray := strings.SplitN(file.Name(), ".", 2)
   165  
   166  		// check that length is 2 as expected, otherwise skip to next cc file
   167  		if len(fileNameArray) == 2 {
   168  			ccname := fileNameArray[0]
   169  			ccversion := fileNameArray[1]
   170  			ccpack, err := GetChaincodeFromFS(ccname, ccversion)
   171  			if err != nil {
   172  				// either chaincode on filesystem has been tampered with or
   173  				// a non-chaincode file has been found in the chaincodes directory
   174  				ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
   175  				continue
   176  			}
   177  
   178  			cdsfs := ccpack.GetDepSpec()
   179  
   180  			name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
   181  			version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
   182  			if name != ccname || version != ccversion {
   183  				// chaincode name/version in the chaincode file name has been modified
   184  				// by an external entity
   185  				ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
   186  				continue
   187  			}
   188  
   189  			path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
   190  			// since this is just an installed chaincode these should be blank
   191  			input, escc, vscc := "", "", ""
   192  
   193  			ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}
   194  
   195  			// add this specific chaincode's metadata to the array of all chaincodes
   196  			ccInfoArray = append(ccInfoArray, ccInfo)
   197  		}
   198  	}
   199  	// add array with info about all instantiated chaincodes to the query
   200  	// response proto
   201  	cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
   202  
   203  	return cqr, nil
   204  }
   205  
   206  //CCContext pass this around instead of string of args
   207  type CCContext struct {
   208  	//ChainID chain id
   209  	ChainID string
   210  
   211  	//Name chaincode name
   212  	Name string
   213  
   214  	//Version used to construct the chaincode image and register
   215  	Version string
   216  
   217  	//TxID is the transaction id for the proposal (if any)
   218  	TxID string
   219  
   220  	//Syscc is this a system chaincode
   221  	Syscc bool
   222  
   223  	//SignedProposal for this invoke (if any)
   224  	//this is kept here for access control and in case we need to pass something
   225  	//from this to the chaincode
   226  	SignedProposal *pb.SignedProposal
   227  
   228  	//Proposal for this invoke (if any)
   229  	//this is kept here just in case we need to pass something
   230  	//from this to the chaincode
   231  	Proposal *pb.Proposal
   232  
   233  	//this is not set but computed (note that this is not exported. use GetCanonicalName)
   234  	canonicalName string
   235  }
   236  
   237  //NewCCContext just construct a new struct with whatever args
   238  func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext {
   239  	//version CANNOT be empty. The chaincode namespace has to use version and chain name.
   240  	//All system chaincodes share the same version given by utils.GetSysCCVersion. Note
   241  	//that neither Chain Name or Version are stored in a chaincodes state on the ledger
   242  	if version == "" {
   243  		panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop))
   244  	}
   245  
   246  	canName := name + ":" + version
   247  
   248  	cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName}
   249  
   250  	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)
   251  
   252  	return cccid
   253  }
   254  
   255  //GetCanonicalName returns the canonical name associated with the proposal context
   256  func (cccid *CCContext) GetCanonicalName() string {
   257  	if cccid.canonicalName == "" {
   258  		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))
   259  	}
   260  
   261  	return cccid.canonicalName
   262  }
   263  
   264  //-------- ChaincodeData is stored on the LSCC -------
   265  
   266  //ChaincodeData defines the datastructure for chaincodes to be serialized by proto
   267  //Type provides an additional check by directing to use a specific package after instantiation
   268  //Data is Type specifc (see CDSPackage and SignedCDSPackage)
   269  type ChaincodeData struct {
   270  	//Name of the chaincode
   271  	Name string `protobuf:"bytes,1,opt,name=name"`
   272  
   273  	//Version of the chaincode
   274  	Version string `protobuf:"bytes,2,opt,name=version"`
   275  
   276  	//Escc for the chaincode instance
   277  	Escc string `protobuf:"bytes,3,opt,name=escc"`
   278  
   279  	//Vscc for the chaincode instance
   280  	Vscc string `protobuf:"bytes,4,opt,name=vscc"`
   281  
   282  	//Policy endorsement policy for the chaincode instance
   283  	Policy []byte `protobuf:"bytes,5,opt,name=policy,proto3"`
   284  
   285  	//Data data specific to the package
   286  	Data []byte `protobuf:"bytes,6,opt,name=data,proto3"`
   287  
   288  	//Id of the chaincode that's the unique fingerprint for the CC
   289  	//This is not currently used anywhere but serves as a good
   290  	//eyecatcher
   291  	Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`
   292  
   293  	//InstantiationPolicy for the chaincode
   294  	InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
   295  }
   296  
   297  //implement functions needed from proto.Message for proto's mar/unmarshal functions
   298  
   299  //Reset resets
   300  func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
   301  
   302  //String convers to string
   303  func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
   304  
   305  //ProtoMessage just exists to make proto happy
   306  func (*ChaincodeData) ProtoMessage() {}
   307  
   308  // ChaincodeProvider provides an abstraction layer that is
   309  // used for different packages to interact with code in the
   310  // chaincode package without importing it; more methods
   311  // should be added below if necessary
   312  type ChaincodeProvider interface {
   313  	// GetContext returns a ledger context
   314  	GetContext(ledger ledger.PeerLedger) (context.Context, error)
   315  	// GetCCContext returns an opaque chaincode context
   316  	GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
   317  	// GetCCValidationInfoFromLSCC returns the VSCC and the policy listed by LSCC for the supplied chaincode
   318  	GetCCValidationInfoFromLSCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
   319  	// ExecuteChaincode executes the chaincode given context and args
   320  	ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
   321  	// Execute executes the chaincode given context and spec (invocation or deploy)
   322  	Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
   323  	// ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload
   324  	ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
   325  	// Stop stops the chaincode given context and deployment spec
   326  	Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
   327  	// ReleaseContext releases the context returned previously by GetContext
   328  	ReleaseContext()
   329  }
   330  
   331  var ccFactory ChaincodeProviderFactory
   332  
   333  // ChaincodeProviderFactory defines a factory interface so
   334  // that the actual implementation can be injected
   335  type ChaincodeProviderFactory interface {
   336  	NewChaincodeProvider() ChaincodeProvider
   337  }
   338  
   339  // RegisterChaincodeProviderFactory is to be called once to set
   340  // the factory that will be used to obtain instances of ChaincodeProvider
   341  func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
   342  	ccFactory = ccfact
   343  }
   344  
   345  // GetChaincodeProvider returns instances of ChaincodeProvider;
   346  // the actual implementation is controlled by the factory that
   347  // is registered via RegisterChaincodeProviderFactory
   348  func GetChaincodeProvider() ChaincodeProvider {
   349  	if ccFactory == nil {
   350  		panic("The factory must be set first via RegisterChaincodeProviderFactory")
   351  	}
   352  	return ccFactory.NewChaincodeProvider()
   353  }