github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/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/osdi23p228/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  	if _, err := proto.Marshal(cds); err != nil {
   131  		return nil, nil, nil, err
   132  	}
   133  
   134  	//compute hashes now
   135  	// hash, err := factory.GetDefault().GetHash(&bccsp.SHAOpts{})
   136  	hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{})
   137  	if err != nil {
   138  		return nil, nil, nil, err
   139  	}
   140  
   141  	cdsdata := &CDSData{}
   142  
   143  	//code hash
   144  	hash.Write(cds.CodePackage)
   145  	cdsdata.CodeHash = hash.Sum(nil)
   146  
   147  	hash.Reset()
   148  
   149  	//metadata hash
   150  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
   151  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
   152  
   153  	cdsdata.MetaDataHash = hash.Sum(nil)
   154  
   155  	b, err := proto.Marshal(cdsdata)
   156  	if err != nil {
   157  		return nil, nil, nil, err
   158  	}
   159  
   160  	hash.Reset()
   161  
   162  	//compute the id
   163  	hash.Write(cdsdata.CodeHash)
   164  	hash.Write(cdsdata.MetaDataHash)
   165  
   166  	id := hash.Sum(nil)
   167  
   168  	return b, id, cdsdata, nil
   169  }
   170  
   171  // ValidateCC returns error if the chaincode is not found or if its not a
   172  // ChaincodeDeploymentSpec
   173  func (ccpack *CDSPackage) ValidateCC(ccdata *ChaincodeData) error {
   174  	if ccpack.depSpec == nil {
   175  		return fmt.Errorf("uninitialized package")
   176  	}
   177  
   178  	if ccpack.data == nil {
   179  		return fmt.Errorf("nil data")
   180  	}
   181  
   182  	// This is a hack. LSCC expects a specific LSCC error when names are invalid so it
   183  	// has its own validation code. We can't use that error because of import cycles.
   184  	// Unfortunately, we also need to check if what have makes some sort of sense as
   185  	// protobuf will gladly deserialize garbage and there are paths where we assume that
   186  	// a successful unmarshal means everything works but, if it fails, we try to unmarshal
   187  	// into something different.
   188  	if !isPrintable(ccdata.Name) {
   189  		return fmt.Errorf("invalid chaincode name: %q", ccdata.Name)
   190  	}
   191  
   192  	if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
   193  		return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
   194  	}
   195  
   196  	otherdata := &CDSData{}
   197  	err := proto.Unmarshal(ccdata.Data, otherdata)
   198  	if err != nil {
   199  		return err
   200  	}
   201  
   202  	if !ccpack.data.Equals(otherdata) {
   203  		return fmt.Errorf("data mismatch")
   204  	}
   205  
   206  	return nil
   207  }
   208  
   209  //InitFromBuffer sets the buffer if valid and returns ChaincodeData
   210  func (ccpack *CDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
   211  
   212  	depSpec := &pb.ChaincodeDeploymentSpec{}
   213  	err := proto.Unmarshal(buf, depSpec)
   214  	if err != nil {
   215  		return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
   216  	}
   217  
   218  	databytes, id, data, err := ccpack.getCDSData(depSpec)
   219  	if err != nil {
   220  		return nil, err
   221  	}
   222  
   223  	ccpack.buf = buf
   224  	ccpack.depSpec = depSpec
   225  	ccpack.data = data
   226  	ccpack.datab = databytes
   227  	ccpack.id = id
   228  
   229  	return ccpack.GetChaincodeData(), nil
   230  }
   231  
   232  //InitFromFS returns the chaincode and its package from the file system
   233  func (ccpack *CDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   234  
   235  	buf, err := GetChaincodePackageFromPath(ccNameVersion, path)
   236  	if err != nil {
   237  		return nil, nil, err
   238  	}
   239  
   240  	ccdata, err := ccpack.InitFromBuffer(buf)
   241  	if err != nil {
   242  		return nil, nil, err
   243  	}
   244  
   245  	if err := ccpack.ValidateCC(ccdata); err != nil {
   246  		return nil, nil, err
   247  	}
   248  
   249  	return ccpack.buf, ccpack.depSpec, nil
   250  }
   251  
   252  //InitFromFS returns the chaincode and its package from the file system
   253  func (ccpack *CDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   254  	return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath)
   255  }
   256  
   257  //PutChaincodeToFS - serializes chaincode to a package on the file system
   258  func (ccpack *CDSPackage) PutChaincodeToFS() error {
   259  	if ccpack.buf == nil {
   260  		return fmt.Errorf("uninitialized package")
   261  	}
   262  
   263  	if ccpack.id == nil {
   264  		return fmt.Errorf("id cannot be nil if buf is not nil")
   265  	}
   266  
   267  	if ccpack.depSpec == nil {
   268  		return fmt.Errorf("depspec cannot be nil if buf is not nil")
   269  	}
   270  
   271  	if ccpack.data == nil {
   272  		return fmt.Errorf("nil data")
   273  	}
   274  
   275  	if ccpack.datab == nil {
   276  		return fmt.Errorf("nil data bytes")
   277  	}
   278  
   279  	ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
   280  	ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
   281  
   282  	//return error if chaincode exists
   283  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
   284  	if _, err := os.Stat(path); err == nil {
   285  		return fmt.Errorf("chaincode %s exists", path)
   286  	}
   287  
   288  	if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil {
   289  		return err
   290  	}
   291  
   292  	return nil
   293  }