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