github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/common/ccprovider/cdspackage.go (about)

     1  /*
     2  Copyright hechain. 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  	"github.com/hechain20/hechain/bccsp"
    28  	pb "github.com/hyperledger/fabric-protos-go/peer"
    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  	depSpec := &pb.ChaincodeDeploymentSpec{}
   212  	err := proto.Unmarshal(buf, depSpec)
   213  	if err != nil {
   214  		return nil, fmt.Errorf("failed to unmarshal deployment spec from bytes")
   215  	}
   216  
   217  	databytes, id, data, err := ccpack.getCDSData(depSpec)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  
   222  	ccpack.buf = buf
   223  	ccpack.depSpec = depSpec
   224  	ccpack.data = data
   225  	ccpack.datab = databytes
   226  	ccpack.id = id
   227  
   228  	return ccpack.GetChaincodeData(), nil
   229  }
   230  
   231  // InitFromFS returns the chaincode and its package from the file system
   232  func (ccpack *CDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   233  	buf, err := GetChaincodePackageFromPath(ccNameVersion, path)
   234  	if err != nil {
   235  		return nil, nil, err
   236  	}
   237  
   238  	ccdata, err := ccpack.InitFromBuffer(buf)
   239  	if err != nil {
   240  		return nil, nil, err
   241  	}
   242  
   243  	if err := ccpack.ValidateCC(ccdata); err != nil {
   244  		return nil, nil, err
   245  	}
   246  
   247  	return ccpack.buf, ccpack.depSpec, nil
   248  }
   249  
   250  // InitFromFS returns the chaincode and its package from the file system
   251  func (ccpack *CDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   252  	return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath)
   253  }
   254  
   255  // PutChaincodeToFS - serializes chaincode to a package on the file system
   256  func (ccpack *CDSPackage) PutChaincodeToFS() error {
   257  	if ccpack.buf == nil {
   258  		return fmt.Errorf("uninitialized package")
   259  	}
   260  
   261  	if ccpack.id == nil {
   262  		return fmt.Errorf("id cannot be nil if buf is not nil")
   263  	}
   264  
   265  	if ccpack.depSpec == nil {
   266  		return fmt.Errorf("depspec cannot be nil if buf is not nil")
   267  	}
   268  
   269  	if ccpack.data == nil {
   270  		return fmt.Errorf("nil data")
   271  	}
   272  
   273  	if ccpack.datab == nil {
   274  		return fmt.Errorf("nil data bytes")
   275  	}
   276  
   277  	ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
   278  	ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
   279  
   280  	// return error if chaincode exists
   281  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
   282  	if _, err := os.Stat(path); err == nil {
   283  		return fmt.Errorf("chaincode %s exists", path)
   284  	}
   285  
   286  	if err := ioutil.WriteFile(path, ccpack.buf, 0o644); err != nil {
   287  		return err
   288  	}
   289  
   290  	return nil
   291  }