github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/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 converts 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  	hash.Write(cds.CodePackage)
   150  	cdsdata.CodeHash = hash.Sum(nil)
   151  
   152  	hash.Reset()
   153  
   154  	//metadata hash
   155  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
   156  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
   157  
   158  	cdsdata.MetaDataHash = hash.Sum(nil)
   159  
   160  	b, err = proto.Marshal(cdsdata)
   161  	if err != nil {
   162  		return nil, nil, nil, err
   163  	}
   164  
   165  	hash.Reset()
   166  
   167  	//compute the id
   168  	hash.Write(cdsdata.CodeHash)
   169  	hash.Write(cdsdata.MetaDataHash)
   170  
   171  	id := hash.Sum(nil)
   172  
   173  	return b, id, cdsdata, nil
   174  }
   175  
   176  // ValidateCC returns error if the chaincode is not found or if its not a
   177  // ChaincodeDeploymentSpec
   178  func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
   179  	if ccpack.depSpec == nil {
   180  		return fmt.Errorf("uninitialized package")
   181  	}
   182  
   183  	if ccpack.data == nil {
   184  		return fmt.Errorf("nil data")
   185  	}
   186  
   187  	if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
   188  		return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
   189  	}
   190  
   191  	otherdata := &CDSData{}
   192  	err := proto.Unmarshal(ccdata.Data, otherdata)
   193  	if err != nil {
   194  		return err
   195  	}
   196  
   197  	if !ccpack.data.Equals(otherdata) {
   198  		return fmt.Errorf("data mismatch")
   199  	}
   200  
   201  	return nil
   202  }
   203  
   204  //InitFromBuffer sets the buffer if valid and returns ChaincodeData
   205  func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
   206  	//incase ccpack is reused
   207  	ccpack.reset()
   208  
   209  	depSpec := &pb.ChaincodeDeploymentSpec{}
   210  	err := proto.Unmarshal(buf, depSpec)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
   213  	}
   214  
   215  	databytes, id, data, err := ccpack.getCDSData(depSpec)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	ccpack.buf = buf
   221  	ccpack.depSpec = depSpec
   222  	ccpack.data = data
   223  	ccpack.datab = databytes
   224  	ccpack.id = id
   225  
   226  	return ccpack.GetChaincodeData(), nil
   227  }
   228  
   229  //InitFromFS returns the chaincode and its package from the file system
   230  func (ccpack *CDSPackage) InitFromFS(ccname string, ccversion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   231  	//incase ccpack is reused
   232  	ccpack.reset()
   233  
   234  	buf, err := GetChaincodePackage(ccname, ccversion)
   235  	if err != nil {
   236  		return nil, nil, err
   237  	}
   238  
   239  	if _, err = ccpack.InitFromBuffer(buf); err != nil {
   240  		return nil, nil, err
   241  	}
   242  
   243  	return ccpack.buf, ccpack.depSpec, nil
   244  }
   245  
   246  //PutChaincodeToFS - serializes chaincode to a package on the file system
   247  func (ccpack *CDSPackage) PutChaincodeToFS() error {
   248  	if ccpack.buf == nil {
   249  		return fmt.Errorf("uninitialized package")
   250  	}
   251  
   252  	if ccpack.id == nil {
   253  		return fmt.Errorf("id cannot be nil if buf is not nil")
   254  	}
   255  
   256  	if ccpack.depSpec == nil {
   257  		return fmt.Errorf("depspec cannot be nil if buf is not nil")
   258  	}
   259  
   260  	if ccpack.data == nil {
   261  		return fmt.Errorf("nil data")
   262  	}
   263  
   264  	if ccpack.datab == nil {
   265  		return fmt.Errorf("nil data bytes")
   266  	}
   267  
   268  	ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
   269  	ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
   270  
   271  	//return error if chaincode exists
   272  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
   273  	if _, err := os.Stat(path); err == nil {
   274  		return fmt.Errorf("chaincode %s exists", path)
   275  	}
   276  
   277  	if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil {
   278  		return err
   279  	}
   280  
   281  	return nil
   282  }