github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/core/common/ccprovider/cdspackage.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  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  
    24  	"github.com/golang/protobuf/proto"
    25  
    26  	"bytes"
    27  
    28  	"github.com/hyperledger/fabric/bccsp"
    29  	"github.com/hyperledger/fabric/bccsp/factory"
    30  	pb "github.com/hyperledger/fabric/protos/peer"
    31  )
    32  
    33  //----- CDSData ------
    34  
    35  //CDSData is data stored in the LSCC on instantiation of a CC
    36  //for CDSPackage.  This needs to be serialized for ChaincodeData
    37  //hence the protobuf format
    38  type CDSData struct {
    39  	//CodeHash hash of CodePackage from ChaincodeDeploymentSpec
    40  	CodeHash []byte `protobuf:"bytes,1,opt,name=codehash,proto3"`
    41  
    42  	//MetaDataHash hash of Name and Version from ChaincodeDeploymentSpec
    43  	MetaDataHash []byte `protobuf:"bytes,2,opt,name=metadatahash,proto3"`
    44  }
    45  
    46  //----implement functions needed from proto.Message for proto's mar/unmarshal functions
    47  
    48  //Reset resets
    49  func (data *CDSData) Reset() { *data = CDSData{} }
    50  
    51  //String convers to string
    52  func (data *CDSData) String() string { return proto.CompactTextString(data) }
    53  
    54  //ProtoMessage just exists to make proto happy
    55  func (*CDSData) ProtoMessage() {}
    56  
    57  //Equals data equals other
    58  func (data *CDSData) Equals(other *CDSData) bool {
    59  	return other != nil && bytes.Equal(data.CodeHash, other.CodeHash) && bytes.Equal(data.MetaDataHash, other.MetaDataHash)
    60  }
    61  
    62  //--------- CDSPackage ------------
    63  
    64  //CDSPackage encapsulates ChaincodeDeploymentSpec.
    65  type CDSPackage struct {
    66  	buf     []byte
    67  	depSpec *pb.ChaincodeDeploymentSpec
    68  	data    *CDSData
    69  	datab   []byte
    70  	id      []byte
    71  }
    72  
    73  // resets data
    74  func (ccpack *CDSPackage) reset() {
    75  	*ccpack = CDSPackage{}
    76  }
    77  
    78  // GetId gets the fingerprint of the chaincode based on package computation
    79  func (ccpack *CDSPackage) GetId() []byte {
    80  	//this has to be after creating a package and initializing it
    81  	//If those steps fail, GetId() should never be called
    82  	if ccpack.id == nil {
    83  		panic("GetId called on uninitialized package")
    84  	}
    85  	return ccpack.id
    86  }
    87  
    88  // GetDepSpec gets the ChaincodeDeploymentSpec from the package
    89  func (ccpack *CDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
    90  	//this has to be after creating a package and initializing it
    91  	//If those steps fail, GetDepSpec() should never be called
    92  	if ccpack.depSpec == nil {
    93  		panic("GetDepSpec called on uninitialized package")
    94  	}
    95  	return ccpack.depSpec
    96  }
    97  
    98  // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
    99  func (ccpack *CDSPackage) GetDepSpecBytes() []byte {
   100  	//this has to be after creating a package and initializing it
   101  	//If those steps fail, GetDepSpecBytes() should never be called
   102  	if ccpack.buf == nil {
   103  		panic("GetDepSpecBytes called on uninitialized package")
   104  	}
   105  	return ccpack.buf
   106  }
   107  
   108  // GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
   109  func (ccpack *CDSPackage) GetPackageObject() proto.Message {
   110  	return ccpack.depSpec
   111  }
   112  
   113  // GetChaincodeData gets the ChaincodeData
   114  func (ccpack *CDSPackage) GetChaincodeData() *ChaincodeData {
   115  	//this has to be after creating a package and initializing it
   116  	//If those steps fail, GetChaincodeData() should never be called
   117  	if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil {
   118  		panic("GetChaincodeData called on uninitialized package")
   119  	}
   120  	return &ChaincodeData{Name: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name, Version: ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version, Data: ccpack.datab, Id: ccpack.id}
   121  }
   122  
   123  func (ccpack *CDSPackage) getCDSData(cds *pb.ChaincodeDeploymentSpec) ([]byte, []byte, *CDSData, error) {
   124  	// check for nil argument. It is an assertion that getCDSData
   125  	// is never called on a package that did not go through/succeed
   126  	// package initialization.
   127  	if cds == nil {
   128  		panic("nil cds")
   129  	}
   130  
   131  	b, err := proto.Marshal(cds)
   132  	if err != nil {
   133  		return nil, nil, nil, err
   134  	}
   135  
   136  	if err = factory.InitFactories(nil); err != nil {
   137  		return nil, nil, nil, fmt.Errorf("Internal error, BCCSP could not be initialized : %s", err)
   138  	}
   139  
   140  	//compute hashes now
   141  	hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
   142  	if err != nil {
   143  		return nil, nil, nil, err
   144  	}
   145  
   146  	cdsdata := &CDSData{}
   147  
   148  	//code hash
   149  	cdsdata.CodeHash = hash.Sum(cds.CodePackage)
   150  
   151  	hash.Reset()
   152  
   153  	//metadata hash
   154  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
   155  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
   156  
   157  	cdsdata.MetaDataHash = hash.Sum(nil)
   158  
   159  	b, err = proto.Marshal(cdsdata)
   160  	if err != nil {
   161  		return nil, nil, nil, err
   162  	}
   163  
   164  	hash.Reset()
   165  
   166  	//compute the id
   167  	hash.Write(cdsdata.CodeHash)
   168  	hash.Write(cdsdata.MetaDataHash)
   169  
   170  	id := hash.Sum(nil)
   171  
   172  	return b, id, cdsdata, nil
   173  }
   174  
   175  // ValidateCC returns error if the chaincode is not found or if its not a
   176  // ChaincodeDeploymentSpec
   177  func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
   178  	if ccpack.depSpec == nil {
   179  		return fmt.Errorf("uninitialized package")
   180  	}
   181  
   182  	if ccpack.data == nil {
   183  		return fmt.Errorf("nil data")
   184  	}
   185  
   186  	if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
   187  		return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
   188  	}
   189  
   190  	otherdata := &CDSData{}
   191  	err := proto.Unmarshal(ccdata.Data, otherdata)
   192  	if err != nil {
   193  		return err
   194  	}
   195  
   196  	if !ccpack.data.Equals(otherdata) {
   197  		return fmt.Errorf("data mismatch")
   198  	}
   199  
   200  	return nil
   201  }
   202  
   203  //InitFromBuffer sets the buffer if valid and returns ChaincodeData
   204  func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
   205  	//incase ccpack is reused
   206  	ccpack.reset()
   207  
   208  	depSpec := &pb.ChaincodeDeploymentSpec{}
   209  	err := proto.Unmarshal(buf, depSpec)
   210  	if err != nil {
   211  		return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
   212  	}
   213  
   214  	databytes, id, data, err := ccpack.getCDSData(depSpec)
   215  	if err != nil {
   216  		return nil, err
   217  	}
   218  
   219  	ccpack.buf = buf
   220  	ccpack.depSpec = depSpec
   221  	ccpack.data = data
   222  	ccpack.datab = databytes
   223  	ccpack.id = id
   224  
   225  	return ccpack.GetChaincodeData(), nil
   226  }
   227  
   228  //InitFromFS returns the chaincode and its package from the file system
   229  func (ccpack *CDSPackage) InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   230  	//incase ccpack is reused
   231  	ccpack.reset()
   232  
   233  	buf, err := GetChaincodePackage(ccname, ccversion)
   234  	if err != nil {
   235  		return nil, nil, err
   236  	}
   237  
   238  	if _, err = ccpack.InitFromBuffer(buf); err != nil {
   239  		return nil, nil, err
   240  	}
   241  
   242  	return ccpack.buf, ccpack.depSpec, nil
   243  }
   244  
   245  //PutChaincodeToFS - serializes chaincode to a package on the file system
   246  func (ccpack *CDSPackage) PutChaincodeToFS() error {
   247  	if ccpack.buf == nil {
   248  		return fmt.Errorf("uninitialized package")
   249  	}
   250  
   251  	if ccpack.id == nil {
   252  		return fmt.Errorf("id cannot be nil if buf is not nil")
   253  	}
   254  
   255  	if ccpack.depSpec == nil {
   256  		return fmt.Errorf("depspec cannot be nil if buf is not nil")
   257  	}
   258  
   259  	if ccpack.data == nil {
   260  		return fmt.Errorf("nil data")
   261  	}
   262  
   263  	if ccpack.datab == nil {
   264  		return fmt.Errorf("nil data bytes")
   265  	}
   266  
   267  	ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
   268  	ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
   269  
   270  	//return error if chaincode exists
   271  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
   272  	if _, err := os.Stat(path); err == nil {
   273  		return fmt.Errorf("chaincode %s exists", path)
   274  	}
   275  
   276  	if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil {
   277  		return err
   278  	}
   279  
   280  	return nil
   281  }