240 lines
8.2 KiB
Go
240 lines
8.2 KiB
Go
/*
|
|
Copyright IBM Corp. 2016-2019 All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package ccpackage
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/hyperledger/fabric-protos-go/common"
|
|
"github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
)
|
|
|
|
// ExtractSignedCCDepSpec extracts the messages from the envelope
|
|
func ExtractSignedCCDepSpec(env *common.Envelope) (*common.ChannelHeader, *peer.SignedChaincodeDeploymentSpec, error) {
|
|
p := &common.Payload{}
|
|
err := proto.Unmarshal(env.Payload, p)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if p.Header == nil {
|
|
return nil, nil, errors.New("channel header cannot be nil")
|
|
}
|
|
ch := &common.ChannelHeader{}
|
|
err = proto.Unmarshal(p.Header.ChannelHeader, ch)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
sp := &peer.SignedChaincodeDeploymentSpec{}
|
|
err = proto.Unmarshal(p.Data, sp)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return ch, sp, nil
|
|
}
|
|
|
|
// This file provides functions for helping with the chaincode install
|
|
// package workflow. In particular
|
|
// OwnerCreateSignedCCDepSpec - each owner creates signs the package using the same deploy
|
|
// CreateSignedCCDepSpecForInstall - an admin or owner creates the package to be installed
|
|
// using the packages from OwnerCreateSignedCCDepSpec
|
|
|
|
// ValidateCip validate the endorsed package against the base package
|
|
func ValidateCip(baseCip, otherCip *peer.SignedChaincodeDeploymentSpec) error {
|
|
if baseCip == nil || otherCip == nil {
|
|
panic("do not call with nil parameters")
|
|
}
|
|
|
|
if (baseCip.OwnerEndorsements == nil && otherCip.OwnerEndorsements != nil) || (baseCip.OwnerEndorsements != nil && otherCip.OwnerEndorsements == nil) {
|
|
return fmt.Errorf("endorsements should either be both nil or not nil")
|
|
}
|
|
|
|
bN := len(baseCip.OwnerEndorsements)
|
|
oN := len(otherCip.OwnerEndorsements)
|
|
if bN > 1 || oN > 1 {
|
|
return fmt.Errorf("expect utmost 1 endorsement from a owner")
|
|
}
|
|
|
|
if bN != oN {
|
|
return fmt.Errorf("Rule-all packages should be endorsed or none should be endorsed failed for (%d, %d)", bN, oN)
|
|
}
|
|
|
|
if !bytes.Equal(baseCip.ChaincodeDeploymentSpec, otherCip.ChaincodeDeploymentSpec) {
|
|
return fmt.Errorf("Rule-all deployment specs should match(%d, %d)", len(baseCip.ChaincodeDeploymentSpec), len(otherCip.ChaincodeDeploymentSpec))
|
|
}
|
|
|
|
if !bytes.Equal(baseCip.InstantiationPolicy, otherCip.InstantiationPolicy) {
|
|
return fmt.Errorf("Rule-all instantiation policies should match(%d, %d)", len(baseCip.InstantiationPolicy), len(otherCip.InstantiationPolicy))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func createSignedCCDepSpec(cdsbytes []byte, instpolicybytes []byte, endorsements []*peer.Endorsement) (*common.Envelope, error) {
|
|
if cdsbytes == nil {
|
|
return nil, fmt.Errorf("nil chaincode deployment spec")
|
|
}
|
|
|
|
if instpolicybytes == nil {
|
|
return nil, fmt.Errorf("nil instantiation policy")
|
|
}
|
|
|
|
// create SignedChaincodeDeploymentSpec...
|
|
cip := &peer.SignedChaincodeDeploymentSpec{ChaincodeDeploymentSpec: cdsbytes, InstantiationPolicy: instpolicybytes, OwnerEndorsements: endorsements}
|
|
|
|
//...and marshal it
|
|
cipbytes := protoutil.MarshalOrPanic(cip)
|
|
|
|
// use defaults (this is definitely ok for install package)
|
|
msgVersion := int32(0)
|
|
epoch := uint64(0)
|
|
chdr := protoutil.MakeChannelHeader(common.HeaderType_CHAINCODE_PACKAGE, msgVersion, "", epoch)
|
|
|
|
// create the payload
|
|
payl := &common.Payload{Header: &common.Header{ChannelHeader: protoutil.MarshalOrPanic(chdr)}, Data: cipbytes}
|
|
paylBytes, err := protoutil.GetBytesPayload(payl)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// here's the unsigned envelope. The install package is endorsed if signingEntity != nil
|
|
return &common.Envelope{Payload: paylBytes}, nil
|
|
}
|
|
|
|
// CreateSignedCCDepSpecForInstall creates the final package from a set of packages signed by
|
|
// owners. This is similar to how the SDK assembles a TX from various proposal
|
|
// responses from the signatures.
|
|
func CreateSignedCCDepSpecForInstall(pack []*common.Envelope) (*common.Envelope, error) {
|
|
if len(pack) == 0 {
|
|
return nil, errors.New("no packages provided to collate")
|
|
}
|
|
|
|
// rules...
|
|
// all packages must be endorsed or all packages should not be endorsed
|
|
// the chaincode deployment spec should be same
|
|
var baseCip *peer.SignedChaincodeDeploymentSpec
|
|
var err error
|
|
var endorsementExists bool
|
|
var endorsements []*peer.Endorsement
|
|
for n, r := range pack {
|
|
p := &common.Payload{}
|
|
if err = proto.Unmarshal(r.Payload, p); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cip := &peer.SignedChaincodeDeploymentSpec{}
|
|
if err = proto.Unmarshal(p.Data, cip); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// if its the first element, check if it has endorsement so we can
|
|
// enforce endorsement rules
|
|
if n == 0 {
|
|
baseCip = cip
|
|
// if it has endorsement, all other owners should have signed too
|
|
if len(cip.OwnerEndorsements) > 0 {
|
|
endorsementExists = true
|
|
endorsements = make([]*peer.Endorsement, len(pack))
|
|
}
|
|
|
|
} else if err = ValidateCip(baseCip, cip); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if endorsementExists {
|
|
endorsements[n] = cip.OwnerEndorsements[0]
|
|
}
|
|
}
|
|
|
|
return createSignedCCDepSpec(baseCip.ChaincodeDeploymentSpec, baseCip.InstantiationPolicy, endorsements)
|
|
}
|
|
|
|
// OwnerCreateSignedCCDepSpec creates a package from a ChaincodeDeploymentSpec and
|
|
// optionally endorses it
|
|
func OwnerCreateSignedCCDepSpec(cds *peer.ChaincodeDeploymentSpec, instPolicy *common.SignaturePolicyEnvelope, owner identity.SignerSerializer) (*common.Envelope, error) {
|
|
if cds == nil {
|
|
return nil, fmt.Errorf("invalid chaincode deployment spec")
|
|
}
|
|
|
|
if instPolicy == nil {
|
|
return nil, fmt.Errorf("must provide an instantiation policy")
|
|
}
|
|
|
|
cdsbytes := protoutil.MarshalOrPanic(cds)
|
|
|
|
instpolicybytes := protoutil.MarshalOrPanic(instPolicy)
|
|
|
|
var endorsements []*peer.Endorsement
|
|
// it is not mandatory (at this protoutil level) to have a signature
|
|
// this is especially convenient during dev/test
|
|
// it may be necessary to enforce it via a policy at a higher level
|
|
if owner != nil {
|
|
// serialize the signing identity
|
|
endorser, err := owner.Serialize()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
|
|
}
|
|
|
|
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
|
|
signature, err := owner.Sign(append(cdsbytes, append(instpolicybytes, endorser...)...))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
|
|
}
|
|
|
|
// each owner starts off the endorsements with one element. All such endorsed
|
|
// packages will be collected in a final package by CreateSignedCCDepSpecForInstall
|
|
// when endorsements will have all the entries
|
|
endorsements = make([]*peer.Endorsement, 1)
|
|
|
|
endorsements[0] = &peer.Endorsement{Signature: signature, Endorser: endorser}
|
|
}
|
|
|
|
return createSignedCCDepSpec(cdsbytes, instpolicybytes, endorsements)
|
|
}
|
|
|
|
// SignExistingPackage adds a signature to a signed package.
|
|
func SignExistingPackage(env *common.Envelope, owner identity.SignerSerializer) (*common.Envelope, error) {
|
|
if owner == nil {
|
|
return nil, fmt.Errorf("owner not provided")
|
|
}
|
|
|
|
ch, sdepspec, err := ExtractSignedCCDepSpec(env)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if ch == nil {
|
|
return nil, fmt.Errorf("channel header not found in the envelope")
|
|
}
|
|
|
|
if sdepspec == nil || sdepspec.ChaincodeDeploymentSpec == nil || sdepspec.InstantiationPolicy == nil || sdepspec.OwnerEndorsements == nil {
|
|
return nil, fmt.Errorf("invalid signed deployment spec")
|
|
}
|
|
|
|
// serialize the signing identity
|
|
endorser, err := owner.Serialize()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Could not serialize the signing identity: %s", err)
|
|
}
|
|
|
|
// sign the concatenation of cds, instpolicy and the serialized endorser identity with this endorser's key
|
|
signature, err := owner.Sign(append(sdepspec.ChaincodeDeploymentSpec, append(sdepspec.InstantiationPolicy, endorser...)...))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Could not sign the ccpackage, err %s", err)
|
|
}
|
|
|
|
endorsements := append(sdepspec.OwnerEndorsements, &peer.Endorsement{Signature: signature, Endorser: endorser})
|
|
|
|
return createSignedCCDepSpec(sdepspec.ChaincodeDeploymentSpec, sdepspec.InstantiationPolicy, endorsements)
|
|
}
|