github.com/kaituanwang/hyperledger@v2.0.1+incompatible/core/common/ccprovider/sigcdspackage.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  	"io/ioutil"
    23  	"os"
    24  
    25  	"github.com/golang/protobuf/proto"
    26  	"github.com/hyperledger/fabric-protos-go/common"
    27  	pb "github.com/hyperledger/fabric-protos-go/peer"
    28  	"github.com/hyperledger/fabric/bccsp"
    29  	"github.com/hyperledger/fabric/bccsp/factory"
    30  	"github.com/hyperledger/fabric/core/common/ccpackage"
    31  )
    32  
    33  //----- SignedCDSData ------
    34  
    35  //SignedCDSData is data stored in the LSCC on instantiation of a CC
    36  //for SignedCDSPackage. This needs to be serialized for ChaincodeData
    37  //hence the protobuf format
    38  type SignedCDSData struct {
    39  	CodeHash      []byte `protobuf:"bytes,1,opt,name=hash"`
    40  	MetaDataHash  []byte `protobuf:"bytes,2,opt,name=metadatahash"`
    41  	SignatureHash []byte `protobuf:"bytes,3,opt,name=signaturehash"`
    42  }
    43  
    44  //----implement functions needed from proto.Message for proto's mar/unmarshal functions
    45  
    46  //Reset resets
    47  func (data *SignedCDSData) Reset() { *data = SignedCDSData{} }
    48  
    49  //String converts to string
    50  func (data *SignedCDSData) String() string { return proto.CompactTextString(data) }
    51  
    52  //ProtoMessage just exists to make proto happy
    53  func (*SignedCDSData) ProtoMessage() {}
    54  
    55  //Equals data equals other
    56  func (data *SignedCDSData) Equals(other *SignedCDSData) bool {
    57  	return other != nil &&
    58  		bytes.Equal(data.CodeHash, other.CodeHash) &&
    59  		bytes.Equal(data.MetaDataHash, other.MetaDataHash) &&
    60  		bytes.Equal(data.SignatureHash, other.SignatureHash)
    61  }
    62  
    63  //-------- SignedCDSPackage ---------
    64  
    65  //SignedCDSPackage encapsulates SignedChaincodeDeploymentSpec.
    66  type SignedCDSPackage struct {
    67  	buf       []byte
    68  	depSpec   *pb.ChaincodeDeploymentSpec
    69  	sDepSpec  *pb.SignedChaincodeDeploymentSpec
    70  	env       *common.Envelope
    71  	data      *SignedCDSData
    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 *SignedCDSPackage) 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 *SignedCDSPackage) 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  // GetInstantiationPolicy gets the instantiation policy from the package
    98  func (ccpack *SignedCDSPackage) GetInstantiationPolicy() []byte {
    99  	if ccpack.sDepSpec == nil {
   100  		panic("GetInstantiationPolicy called on uninitialized package")
   101  	}
   102  	return ccpack.sDepSpec.InstantiationPolicy
   103  }
   104  
   105  // GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
   106  func (ccpack *SignedCDSPackage) GetDepSpecBytes() []byte {
   107  	//this has to be after creating a package and initializing it
   108  	//If those steps fail, GetDepSpecBytes() should never be called
   109  	if ccpack.sDepSpec == nil || ccpack.sDepSpec.ChaincodeDeploymentSpec == nil {
   110  		panic("GetDepSpecBytes called on uninitialized package")
   111  	}
   112  	return ccpack.sDepSpec.ChaincodeDeploymentSpec
   113  }
   114  
   115  // GetPackageObject gets the ChaincodeDeploymentSpec as proto.Message
   116  func (ccpack *SignedCDSPackage) GetPackageObject() proto.Message {
   117  	return ccpack.env
   118  }
   119  
   120  // GetChaincodeData gets the ChaincodeData
   121  func (ccpack *SignedCDSPackage) GetChaincodeData() *ChaincodeData {
   122  	//this has to be after creating a package and initializing it
   123  	//If those steps fail, GetChaincodeData() should never be called
   124  	if ccpack.depSpec == nil || ccpack.datab == nil || ccpack.id == nil {
   125  		panic("GetChaincodeData called on uninitialized package")
   126  	}
   127  
   128  	var instPolicy []byte
   129  	if ccpack.sDepSpec != nil {
   130  		instPolicy = ccpack.sDepSpec.InstantiationPolicy
   131  	}
   132  
   133  	return &ChaincodeData{
   134  		Name:                ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name,
   135  		Version:             ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version,
   136  		Data:                ccpack.datab,
   137  		Id:                  ccpack.id,
   138  		InstantiationPolicy: instPolicy,
   139  	}
   140  }
   141  
   142  func (ccpack *SignedCDSPackage) getCDSData(scds *pb.SignedChaincodeDeploymentSpec) ([]byte, []byte, *SignedCDSData, error) {
   143  	// check for nil argument. It is an assertion that getCDSData
   144  	// is never called on a package that did not go through/succeed
   145  	// package initialization.
   146  	if scds == nil {
   147  		panic("nil cds")
   148  	}
   149  
   150  	cds := &pb.ChaincodeDeploymentSpec{}
   151  	err := proto.Unmarshal(scds.ChaincodeDeploymentSpec, cds)
   152  	if err != nil {
   153  		return nil, nil, nil, err
   154  	}
   155  
   156  	if err = factory.InitFactories(nil); err != nil {
   157  		return nil, nil, nil, fmt.Errorf("Internal error, BCCSP could not be initialized : %s", err)
   158  	}
   159  
   160  	//get the hash object
   161  	hash, err := ccpack.GetHasher.GetHash(&bccsp.SHAOpts{})
   162  	if err != nil {
   163  		return nil, nil, nil, err
   164  	}
   165  
   166  	scdsdata := &SignedCDSData{}
   167  
   168  	//get the code hash
   169  	hash.Write(cds.CodePackage)
   170  	scdsdata.CodeHash = hash.Sum(nil)
   171  
   172  	hash.Reset()
   173  
   174  	//get the metadata hash
   175  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Name))
   176  	hash.Write([]byte(cds.ChaincodeSpec.ChaincodeId.Version))
   177  
   178  	scdsdata.MetaDataHash = hash.Sum(nil)
   179  
   180  	hash.Reset()
   181  
   182  	//get the signature hashes
   183  	if scds.InstantiationPolicy == nil {
   184  		return nil, nil, nil, fmt.Errorf("instantiation policy cannot be nil for chaincode (%s:%s)", cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version)
   185  	}
   186  
   187  	hash.Write(scds.InstantiationPolicy)
   188  	for _, o := range scds.OwnerEndorsements {
   189  		hash.Write(o.Endorser)
   190  	}
   191  	scdsdata.SignatureHash = hash.Sum(nil)
   192  
   193  	//marshall data
   194  	b, err := proto.Marshal(scdsdata)
   195  	if err != nil {
   196  		return nil, nil, nil, err
   197  	}
   198  
   199  	hash.Reset()
   200  
   201  	//compute the id
   202  	hash.Write(scdsdata.CodeHash)
   203  	hash.Write(scdsdata.MetaDataHash)
   204  	hash.Write(scdsdata.SignatureHash)
   205  
   206  	id := hash.Sum(nil)
   207  
   208  	return b, id, scdsdata, nil
   209  }
   210  
   211  // ValidateCC returns error if the chaincode is not found or if its not a
   212  // ChaincodeDeploymentSpec
   213  func (ccpack *SignedCDSPackage) ValidateCC(ccdata *ChaincodeData) error {
   214  	if ccpack.sDepSpec == nil {
   215  		return fmt.Errorf("uninitialized package")
   216  	}
   217  
   218  	if ccpack.sDepSpec.ChaincodeDeploymentSpec == nil {
   219  		return fmt.Errorf("signed chaincode deployment spec cannot be nil in a package")
   220  	}
   221  
   222  	if ccpack.depSpec == nil {
   223  		return fmt.Errorf("chaincode deployment spec cannot be nil in a package")
   224  	}
   225  
   226  	// This is a hack. LSCC expects a specific LSCC error when names are invalid so it
   227  	// has its own validation code. We can't use that error because of import cycles.
   228  	// Unfortunately, we also need to check if what have makes some sort of sense as
   229  	// protobuf will gladly deserialize garbage and there are paths where we assume that
   230  	// a successful unmarshal means everything works but, if it fails, we try to unmarshal
   231  	// into something different.
   232  	if !isPrintable(ccdata.Name) {
   233  		return fmt.Errorf("invalid chaincode name: %q", ccdata.Name)
   234  	}
   235  
   236  	if ccdata.Name != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name || ccdata.Version != ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version {
   237  		return fmt.Errorf("invalid chaincode data %v (%v)", ccdata, ccpack.depSpec.ChaincodeSpec.ChaincodeId)
   238  	}
   239  
   240  	otherdata := &SignedCDSData{}
   241  	err := proto.Unmarshal(ccdata.Data, otherdata)
   242  	if err != nil {
   243  		return err
   244  	}
   245  
   246  	if !ccpack.data.Equals(otherdata) {
   247  		return fmt.Errorf("data mismatch")
   248  	}
   249  
   250  	return nil
   251  }
   252  
   253  //InitFromBuffer sets the buffer if valid and returns ChaincodeData
   254  func (ccpack *SignedCDSPackage) InitFromBuffer(buf []byte) (*ChaincodeData, error) {
   255  
   256  	env := &common.Envelope{}
   257  	err := proto.Unmarshal(buf, env)
   258  	if err != nil {
   259  		return nil, fmt.Errorf("failed to unmarshal envelope from bytes")
   260  	}
   261  	cHdr, sDepSpec, err := ccpackage.ExtractSignedCCDepSpec(env)
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  
   266  	if cHdr.Type != int32(common.HeaderType_CHAINCODE_PACKAGE) {
   267  		return nil, fmt.Errorf("invalid type of envelope for chaincode package")
   268  	}
   269  
   270  	depSpec := &pb.ChaincodeDeploymentSpec{}
   271  	err = proto.Unmarshal(sDepSpec.ChaincodeDeploymentSpec, depSpec)
   272  	if err != nil {
   273  		return nil, fmt.Errorf("error getting deployment spec")
   274  	}
   275  
   276  	databytes, id, data, err := ccpack.getCDSData(sDepSpec)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	ccpack.buf = buf
   282  	ccpack.sDepSpec = sDepSpec
   283  	ccpack.depSpec = depSpec
   284  	ccpack.env = env
   285  	ccpack.data = data
   286  	ccpack.datab = databytes
   287  	ccpack.id = id
   288  
   289  	return ccpack.GetChaincodeData(), nil
   290  }
   291  
   292  //InitFromFS returns the chaincode and its package from the file system
   293  func (ccpack *SignedCDSPackage) InitFromFS(ccNameVersion string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   294  	return ccpack.InitFromPath(ccNameVersion, chaincodeInstallPath)
   295  }
   296  
   297  //InitFromPath returns the chaincode and its package from the file system
   298  func (ccpack *SignedCDSPackage) InitFromPath(ccNameVersion string, path string) ([]byte, *pb.ChaincodeDeploymentSpec, error) {
   299  
   300  	buf, err := GetChaincodePackageFromPath(ccNameVersion, path)
   301  	if err != nil {
   302  		return nil, nil, err
   303  	}
   304  
   305  	if _, err = ccpack.InitFromBuffer(buf); err != nil {
   306  		return nil, nil, err
   307  	}
   308  
   309  	return ccpack.buf, ccpack.depSpec, nil
   310  }
   311  
   312  //PutChaincodeToFS - serializes chaincode to a package on the file system
   313  func (ccpack *SignedCDSPackage) PutChaincodeToFS() error {
   314  	if ccpack.buf == nil {
   315  		return fmt.Errorf("uninitialized package")
   316  	}
   317  
   318  	if ccpack.id == nil {
   319  		return fmt.Errorf("id cannot be nil if buf is not nil")
   320  	}
   321  
   322  	if ccpack.sDepSpec == nil || ccpack.depSpec == nil {
   323  		return fmt.Errorf("depspec cannot be nil if buf is not nil")
   324  	}
   325  
   326  	if ccpack.env == nil {
   327  		return fmt.Errorf("env cannot be nil if buf and depspec are not nil")
   328  	}
   329  
   330  	if ccpack.data == nil {
   331  		return fmt.Errorf("nil data")
   332  	}
   333  
   334  	if ccpack.datab == nil {
   335  		return fmt.Errorf("nil data bytes")
   336  	}
   337  
   338  	ccname := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Name
   339  	ccversion := ccpack.depSpec.ChaincodeSpec.ChaincodeId.Version
   340  
   341  	//return error if chaincode exists
   342  	path := fmt.Sprintf("%s/%s.%s", chaincodeInstallPath, ccname, ccversion)
   343  	if _, err := os.Stat(path); err == nil {
   344  		return fmt.Errorf("chaincode %s exists", path)
   345  	}
   346  
   347  	if err := ioutil.WriteFile(path, ccpack.buf, 0644); err != nil {
   348  		return err
   349  	}
   350  
   351  	return nil
   352  }