github.com/sykesm/fabric@v1.1.0-preview.0.20200129034918-2aa12b1a0181/core/common/ccpackage/ccpackage.go (about)

     1  /*
     2  Copyright IBM Corp. 2016-2019 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ccpackage
     8  
     9  import (
    10  	"bytes"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"github.com/golang/protobuf/proto"
    15  	"github.com/hyperledger/fabric-protos-go/common"
    16  	"github.com/hyperledger/fabric-protos-go/peer"
    17  	"github.com/hyperledger/fabric/internal/pkg/identity"
    18  	"github.com/hyperledger/fabric/protoutil"
    19  )
    20  
    21  // ExtractSignedCCDepSpec extracts the messages from the envelope
    22  func ExtractSignedCCDepSpec(env *common.Envelope) (*common.ChannelHeader, *peer.SignedChaincodeDeploymentSpec, error) {
    23  	p := &common.Payload{}
    24  	err := proto.Unmarshal(env.Payload, p)
    25  	if err != nil {
    26  		return nil, nil, err
    27  	}
    28  	if p.Header == nil {
    29  		return nil, nil, errors.New("channel header cannot be nil")
    30  	}
    31  	ch := &common.ChannelHeader{}
    32  	err = proto.Unmarshal(p.Header.ChannelHeader, ch)
    33  	if err != nil {
    34  		return nil, nil, err
    35  	}
    36  
    37  	sp := &peer.SignedChaincodeDeploymentSpec{}
    38  	err = proto.Unmarshal(p.Data, sp)
    39  	if err != nil {
    40  		return nil, nil, err
    41  	}
    42  
    43  	return ch, sp, nil
    44  }
    45  
    46  // This file provides functions for helping with the chaincode install
    47  // package workflow. In particular
    48  //     OwnerCreateSignedCCDepSpec - each owner creates signs the package using the same deploy
    49  //     CreateSignedCCDepSpecForInstall - an admin or owner creates the package to be installed
    50  //                                       using the packages from OwnerCreateSignedCCDepSpec
    51  
    52  // ValidateCip validate the endorsed package against the base package
    53  func ValidateCip(baseCip, otherCip *peer.SignedChaincodeDeploymentSpec) error {
    54  	if baseCip == nil || otherCip == nil {
    55  		panic("do not call with nil parameters")
    56  	}
    57  
    58  	if (baseCip.OwnerEndorsements == nil && otherCip.OwnerEndorsements != nil) || (baseCip.OwnerEndorsements != nil && otherCip.OwnerEndorsements == nil) {
    59  		return fmt.Errorf("endorsements should either be both nil or not nil")
    60  	}
    61  
    62  	bN := len(baseCip.OwnerEndorsements)
    63  	oN := len(otherCip.OwnerEndorsements)
    64  	if bN > 1 || oN > 1 {
    65  		return fmt.Errorf("expect utmost 1 endorsement from a owner")
    66  	}
    67  
    68  	if bN != oN {
    69  		return fmt.Errorf("Rule-all packages should be endorsed or none should be endorsed failed for (%d, %d)", bN, oN)
    70  	}
    71  
    72  	if !bytes.Equal(baseCip.ChaincodeDeploymentSpec, otherCip.ChaincodeDeploymentSpec) {
    73  		return fmt.Errorf("Rule-all deployment specs should match(%d, %d)", len(baseCip.ChaincodeDeploymentSpec), len(otherCip.ChaincodeDeploymentSpec))
    74  	}
    75  
    76  	if !bytes.Equal(baseCip.InstantiationPolicy, otherCip.InstantiationPolicy) {
    77  		return fmt.Errorf("Rule-all instantiation policies should match(%d, %d)", len(baseCip.InstantiationPolicy), len(otherCip.InstantiationPolicy))
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func createSignedCCDepSpec(cdsbytes []byte, instpolicybytes []byte, endorsements []*peer.Endorsement) (*common.Envelope, error) {
    84  	if cdsbytes == nil {
    85  		return nil, fmt.Errorf("nil chaincode deployment spec")
    86  	}
    87  
    88  	if instpolicybytes == nil {
    89  		return nil, fmt.Errorf("nil instantiation policy")
    90  	}
    91  
    92  	// create SignedChaincodeDeploymentSpec...
    93  	cip := &peer.SignedChaincodeDeploymentSpec{ChaincodeDeploymentSpec: cdsbytes, InstantiationPolicy: instpolicybytes, OwnerEndorsements: endorsements}
    94  
    95  	//...and marshal it
    96  	cipbytes := protoutil.MarshalOrPanic(cip)
    97  
    98  	//use defaults (this is definitely ok for install package)
    99  	msgVersion := int32(0)
   100  	epoch := uint64(0)
   101  	chdr := protoutil.MakeChannelHeader(common.HeaderType_CHAINCODE_PACKAGE, msgVersion, "", epoch)
   102  
   103  	// create the payload
   104  	payl := &common.Payload{Header: &common.Header{ChannelHeader: protoutil.MarshalOrPanic(chdr)}, Data: cipbytes}
   105  	paylBytes, err := protoutil.GetBytesPayload(payl)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	// here's the unsigned  envelope. The install package is endorsed if signingEntity != nil
   111  	return &common.Envelope{Payload: paylBytes}, nil
   112  }
   113  
   114  // CreateSignedCCDepSpecForInstall creates the final package from a set of packages signed by
   115  // owners.  This is similar to how the SDK assembles a TX from various proposal
   116  // responses from the signatures.
   117  func CreateSignedCCDepSpecForInstall(pack []*common.Envelope) (*common.Envelope, error) {
   118  	if len(pack) == 0 {
   119  		return nil, errors.New("no packages provided to collate")
   120  	}
   121  
   122  	//rules...
   123  	//   all packages must be endorsed or all packages should not be endorsed
   124  	//   the chaincode deployment spec should be same
   125  	var baseCip *peer.SignedChaincodeDeploymentSpec
   126  	var err error
   127  	var endorsementExists bool
   128  	var endorsements []*peer.Endorsement
   129  	for n, r := range pack {
   130  		p := &common.Payload{}
   131  		if err = proto.Unmarshal(r.Payload, p); err != nil {
   132  			return nil, err
   133  		}
   134  
   135  		cip := &peer.SignedChaincodeDeploymentSpec{}
   136  		if err = proto.Unmarshal(p.Data, cip); err != nil {
   137  			return nil, err
   138  		}
   139  
   140  		//if its the first element, check if it has endorsement so we can
   141  		//enforce endorsement rules
   142  		if n == 0 {
   143  			baseCip = cip
   144  			//if it has endorsement, all other owners should have signed too
   145  			if len(cip.OwnerEndorsements) > 0 {
   146  				endorsementExists = true
   147  				endorsements = make([]*peer.Endorsement, len(pack))
   148  			}
   149  
   150  		} else if err = ValidateCip(baseCip, cip); err != nil {
   151  			return nil, err
   152  		}
   153  
   154  		if endorsementExists {
   155  			endorsements[n] = cip.OwnerEndorsements[0]
   156  		}
   157  	}
   158  
   159  	return createSignedCCDepSpec(baseCip.ChaincodeDeploymentSpec, baseCip.InstantiationPolicy, endorsements)
   160  }
   161  
   162  // OwnerCreateSignedCCDepSpec creates a package from a ChaincodeDeploymentSpec and
   163  // optionally endorses it
   164  func OwnerCreateSignedCCDepSpec(cds *peer.ChaincodeDeploymentSpec, instPolicy *common.SignaturePolicyEnvelope, owner identity.SignerSerializer) (*common.Envelope, error) {
   165  	if cds == nil {
   166  		return nil, fmt.Errorf("invalid chaincode deployment spec")
   167  	}
   168  
   169  	if instPolicy == nil {
   170  		return nil, fmt.Errorf("must provide an instantiation policy")
   171  	}
   172  
   173  	cdsbytes := protoutil.MarshalOrPanic(cds)
   174  
   175  	instpolicybytes := protoutil.MarshalOrPanic(instPolicy)
   176  
   177  	var endorsements []*peer.Endorsement
   178  	//it is not mandatory (at this protoutil level) to have a signature
   179  	//this is especially convenient during dev/test
   180  	//it may be necessary to enforce it via a policy at a higher level
   181  	if owner != nil {
   182  		// serialize the signing identity
   183  		endorser, err := owner.Serialize()
   184  		if err != nil {
   185  			return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
   186  		}
   187  
   188  		// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
   189  		signature, err := owner.Sign(append(cdsbytes, append(instpolicybytes, endorser...)...))
   190  		if err != nil {
   191  			return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
   192  		}
   193  
   194  		// each owner starts off the endorsements with one element. All such endorsed
   195  		// packages will be collected in a final package by CreateSignedCCDepSpecForInstall
   196  		// when endorsements will have all the entries
   197  		endorsements = make([]*peer.Endorsement, 1)
   198  
   199  		endorsements[0] = &peer.Endorsement{Signature: signature, Endorser: endorser}
   200  	}
   201  
   202  	return createSignedCCDepSpec(cdsbytes, instpolicybytes, endorsements)
   203  }
   204  
   205  // SignExistingPackage adds a signature to a signed package.
   206  func SignExistingPackage(env *common.Envelope, owner identity.SignerSerializer) (*common.Envelope, error) {
   207  	if owner == nil {
   208  		return nil, fmt.Errorf("owner not provided")
   209  	}
   210  
   211  	ch, sdepspec, err := ExtractSignedCCDepSpec(env)
   212  	if err != nil {
   213  		return nil, err
   214  	}
   215  
   216  	if ch == nil {
   217  		return nil, fmt.Errorf("channel header not found in the envelope")
   218  	}
   219  
   220  	if sdepspec == nil || sdepspec.ChaincodeDeploymentSpec == nil || sdepspec.InstantiationPolicy == nil || sdepspec.OwnerEndorsements == nil {
   221  		return nil, fmt.Errorf("invalid signed deployment spec")
   222  	}
   223  
   224  	// serialize the signing identity
   225  	endorser, err := owner.Serialize()
   226  	if err != nil {
   227  		return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
   228  	}
   229  
   230  	// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
   231  	signature, err := owner.Sign(append(sdepspec.ChaincodeDeploymentSpec, append(sdepspec.InstantiationPolicy, endorser...)...))
   232  	if err != nil {
   233  		return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
   234  	}
   235  
   236  	endorsements := append(sdepspec.OwnerEndorsements, &peer.Endorsement{Signature: signature, Endorser: endorser})
   237  
   238  	return createSignedCCDepSpec(sdepspec.ChaincodeDeploymentSpec, sdepspec.InstantiationPolicy, endorsements)
   239  }