github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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/core/ledger"
    29  	pb "github.com/hyperledger/fabric/protos/peer"
    30  
    31  	logging "github.com/op/go-logging"
    32  )
    33  
    34  var ccproviderLogger = logging.MustGetLogger("ccprovider")
    35  
    36  var chaincodeInstallPath string
    37  
    38  //SetChaincodesPath sets the chaincode path for this peer
    39  func SetChaincodesPath(path string) {
    40  	if s, err := os.Stat(path); err != nil {
    41  		if os.IsNotExist(err) {
    42  			if err := os.Mkdir(path, 0755); err != nil {
    43  				panic(fmt.Sprintf("Could not create chaincodes install path: %s", err))
    44  			}
    45  		} else {
    46  			panic(fmt.Sprintf("Could not stat chaincodes install path: %s", err))
    47  		}
    48  	} else if !s.IsDir() {
    49  		panic(fmt.Errorf("chaincode path exists but not a dir: %s", path))
    50  	}
    51  
    52  	chaincodeInstallPath = path
    53  }
    54  
    55  //GetChaincodePackage returns the chaincode package from the file system
    56  func GetChaincodePackage(ccname string, ccversion string) ([]byte, error) {
    57  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
    58  	var ccbytes []byte
    59  	var err error
    60  	if ccbytes, err = ioutil.ReadFile(path); err != nil {
    61  		return nil, err
    62  	}
    63  	return ccbytes, nil
    64  }
    65  
    66  //GetChaincodeFromFS returns the chaincode and its package from the file system
    67  func GetChaincodeFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
    68  	//NOTE- this is the only place from where we get code from file system
    69  	//this API needs to be modified to take other params for security.
    70  	//this implementation needs to be enhanced to do those security checks
    71  	ccbytes, err := GetChaincodePackage(ccname, ccversion)
    72  	if err != nil {
    73  		return nil, nil, err
    74  	}
    75  
    76  	cdsfs := &pb.ChaincodeDeploymentSpec{}
    77  	err = proto.Unmarshal(ccbytes, cdsfs)
    78  	if err != nil {
    79  		return nil, nil, fmt.Errorf("failed to unmarshal fs deployment spec for %s, %s", ccname, ccversion)
    80  	}
    81  
    82  	return ccbytes, cdsfs, nil
    83  }
    84  
    85  //PutChaincodeIntoFS - serializes chaincode to a package on the file system
    86  func PutChaincodeIntoFS(depSpec *pb.ChaincodeDeploymentSpec) error {
    87  	//NOTE- this is  only place from where we put code into file system
    88  	//this API needs to be modified to take other params for security.
    89  	//this implementation needs to be enhanced to do those security checks
    90  	ccname := depSpec.ChaincodeSpec.ChaincodeId.Name
    91  	ccversion := depSpec.ChaincodeSpec.ChaincodeId.Version
    92  
    93  	//return error if chaincode exists
    94  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
    95  	if _, err := os.Stat(path); err == nil {
    96  		return fmt.Errorf("chaincode %s exists", path)
    97  	}
    98  
    99  	b, err := proto.Marshal(depSpec)
   100  	if err != nil {
   101  		return fmt.Errorf("failed to marshal fs deployment spec for %s, %s", ccname, ccversion)
   102  	}
   103  
   104  	if err = ioutil.WriteFile(path, b, 0644); err != nil {
   105  		return err
   106  	}
   107  
   108  	return nil
   109  }
   110  
   111  // GetInstalledChaincodes returns a map whose key is the chaincode id and
   112  // value is the ChaincodeDeploymentSpec struct for that chaincodes that have
   113  // been installed (but not necessarily instantiated) on the peer by searching
   114  // the chaincode install path
   115  func GetInstalledChaincodes() (*pb.ChaincodeQueryResponse, error) {
   116  	files, err := ioutil.ReadDir(chaincodeInstallPath)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	// array to store info for all chaincode entries from LCCC
   122  	var ccInfoArray []*pb.ChaincodeInfo
   123  
   124  	for _, file := range files {
   125  		fileNameArray := strings.Split(file.Name(), ".")
   126  
   127  		// check that length is 2 as expected, otherwise skip to next cc file
   128  		if len(fileNameArray) == 2 {
   129  			ccname := fileNameArray[0]
   130  			ccversion := fileNameArray[1]
   131  			_, cdsfs, err := GetChaincodeFromFS(ccname, ccversion)
   132  			if err != nil {
   133  				// either chaincode on filesystem has been tampered with or
   134  				// a non-chaincode file has been found in the chaincodes directory
   135  				ccproviderLogger.Errorf("Unreadable chaincode file found on filesystem: %s", file.Name())
   136  				continue
   137  			}
   138  
   139  			name := cdsfs.GetChaincodeSpec().GetChaincodeId().Name
   140  			version := cdsfs.GetChaincodeSpec().GetChaincodeId().Version
   141  			if name != ccname || version != ccversion {
   142  				// chaincode name/version in the chaincode file name has been modified
   143  				// by an external entity
   144  				ccproviderLogger.Errorf("Chaincode file's name/version has been modified on the filesystem: %s", file.Name())
   145  				continue
   146  			}
   147  
   148  			path := cdsfs.GetChaincodeSpec().ChaincodeId.Path
   149  			// since this is just an installed chaincode these should be blank
   150  			input, escc, vscc := "", "", ""
   151  
   152  			ccInfo := &pb.ChaincodeInfo{Name: name, Version: version, Path: path, Input: input, Escc: escc, Vscc: vscc}
   153  
   154  			// add this specific chaincode's metadata to the array of all chaincodes
   155  			ccInfoArray = append(ccInfoArray, ccInfo)
   156  		}
   157  	}
   158  	// add array with info about all instantiated chaincodes to the query
   159  	// response proto
   160  	cqr := &pb.ChaincodeQueryResponse{Chaincodes: ccInfoArray}
   161  
   162  	return cqr, nil
   163  }
   164  
   165  //CCContext pass this around instead of string of args
   166  type CCContext struct {
   167  	//ChainID chain id
   168  	ChainID string
   169  
   170  	//Name chaincode name
   171  	Name string
   172  
   173  	//Version used to construct the chaincode image and register
   174  	Version string
   175  
   176  	//TxID is the transaction id for the proposal (if any)
   177  	TxID string
   178  
   179  	//Syscc is this a system chaincode
   180  	Syscc bool
   181  
   182  	//SignedProposal for this invoke (if any)
   183  	//this is kept here for access control and in case we need to pass something
   184  	//from this to the chaincode
   185  	SignedProposal *pb.SignedProposal
   186  
   187  	//Proposal for this invoke (if any)
   188  	//this is kept here just in case we need to pass something
   189  	//from this to the chaincode
   190  	Proposal *pb.Proposal
   191  
   192  	//this is not set but computed (note that this is not exported. use GetCanonicalName)
   193  	canonicalName string
   194  }
   195  
   196  //NewCCContext just construct a new struct with whatever args
   197  func NewCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) *CCContext {
   198  	//version CANNOT be empty. The chaincode namespace has to use version and chain name.
   199  	//All system chaincodes share the same version given by utils.GetSysCCVersion. Note
   200  	//that neither Chain Name or Version are stored in a chaincodes state on the ledger
   201  	if version == "" {
   202  		panic(fmt.Sprintf("---empty version---(chain=%s,chaincode=%s,version=%s,txid=%s,syscc=%t,proposal=%p", cid, name, version, txid, syscc, prop))
   203  	}
   204  
   205  	canName := name + ":" + version
   206  
   207  	cccid := &CCContext{cid, name, version, txid, syscc, signedProp, prop, canName}
   208  
   209  	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)
   210  
   211  	return cccid
   212  }
   213  
   214  //GetCanonicalName returns the canonical name associated with the proposal context
   215  func (cccid *CCContext) GetCanonicalName() string {
   216  	if cccid.canonicalName == "" {
   217  		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))
   218  	}
   219  
   220  	return cccid.canonicalName
   221  }
   222  
   223  //ChaincodeData defines the datastructure for chaincodes to be serialized by proto
   224  type ChaincodeData struct {
   225  	Name    string `protobuf:"bytes,1,opt,name=name"`
   226  	Version string `protobuf:"bytes,2,opt,name=version"`
   227  	DepSpec []byte `protobuf:"bytes,3,opt,name=depSpec,proto3"`
   228  	Escc    string `protobuf:"bytes,4,opt,name=escc"`
   229  	Vscc    string `protobuf:"bytes,5,opt,name=vscc"`
   230  	Policy  []byte `protobuf:"bytes,6,opt,name=policy"`
   231  }
   232  
   233  //implement functions needed from proto.Message for proto's mar/unmarshal functions
   234  
   235  //Reset resets
   236  func (cd *ChaincodeData) Reset() { *cd = ChaincodeData{} }
   237  
   238  //String convers to string
   239  func (cd *ChaincodeData) String() string { return proto.CompactTextString(cd) }
   240  
   241  //ProtoMessage just exists to make proto happy
   242  func (*ChaincodeData) ProtoMessage() {}
   243  
   244  // ChaincodeProvider provides an abstraction layer that is
   245  // used for different packages to interact with code in the
   246  // chaincode package without importing it; more methods
   247  // should be added below if necessary
   248  type ChaincodeProvider interface {
   249  	// GetContext returns a ledger context
   250  	GetContext(ledger ledger.PeerLedger) (context.Context, error)
   251  	// GetCCContext returns an opaque chaincode context
   252  	GetCCContext(cid, name, version, txid string, syscc bool, signedProp *pb.SignedProposal, prop *pb.Proposal) interface{}
   253  	// GetCCValidationInfoFromLCCC returns the VSCC and the policy listed by LCCC for the supplied chaincode
   254  	GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error)
   255  	// ExecuteChaincode executes the chaincode given context and args
   256  	ExecuteChaincode(ctxt context.Context, cccid interface{}, args [][]byte) (*pb.Response, *pb.ChaincodeEvent, error)
   257  	// Execute executes the chaincode given context and spec (invocation or deploy)
   258  	Execute(ctxt context.Context, cccid interface{}, spec interface{}) (*pb.Response, *pb.ChaincodeEvent, error)
   259  	// ExecuteWithErrorFilder executes the chaincode given context and spec and returns payload
   260  	ExecuteWithErrorFilter(ctxt context.Context, cccid interface{}, spec interface{}) ([]byte, *pb.ChaincodeEvent, error)
   261  	// Stop stops the chaincode given context and deployment spec
   262  	Stop(ctxt context.Context, cccid interface{}, spec *pb.ChaincodeDeploymentSpec) error
   263  	// ReleaseContext releases the context returned previously by GetContext
   264  	ReleaseContext()
   265  }
   266  
   267  var ccFactory ChaincodeProviderFactory
   268  
   269  // ChaincodeProviderFactory defines a factory interface so
   270  // that the actual implementation can be injected
   271  type ChaincodeProviderFactory interface {
   272  	NewChaincodeProvider() ChaincodeProvider
   273  }
   274  
   275  // RegisterChaincodeProviderFactory is to be called once to set
   276  // the factory that will be used to obtain instances of ChaincodeProvider
   277  func RegisterChaincodeProviderFactory(ccfact ChaincodeProviderFactory) {
   278  	ccFactory = ccfact
   279  }
   280  
   281  // GetChaincodeProvider returns instances of ChaincodeProvider;
   282  // the actual implementation is controlled by the factory that
   283  // is registered via RegisterChaincodeProviderFactory
   284  func GetChaincodeProvider() ChaincodeProvider {
   285  	if ccFactory == nil {
   286  		panic("The factory must be set first via RegisterChaincodeProviderFactory")
   287  	}
   288  	return ccFactory.NewChaincodeProvider()
   289  }