Skip to content

Commit

Permalink
[FAB-2931] CC instantiation tx validation
Browse files Browse the repository at this point in the history
This change adds checks that evaluate the chaincode instantiation policy from
the chaincode package against the instantiation/upgrade proposal submitter at
instantiation/upgrade endorsement time.

For instantiate, the instantiation policy is fetched from the cc package on
disk. In addition, the instantiation policy is added to the ChaincodeData
struct, which is written to the LSCC state. This is necessary to have
the policy available for a subsequent chaincode upgrade.
On chaincode upgrade, the submitter is evaluated against the instantiation
policy of the currently instantiated chaincode. Next, the submitter is
evaluated against the instantiation policy of the new chaincode. If both
checks pass, the instantiation policy of the new chaincode replaces the
previous instantiation policy.

Unit tests for instantiate and upgrade are implemented as part of the LSCC tests.
To test manually on a runnning system, use the CLI to package, sign, install,
instantiate and upgrade chaincode with instantiation policies. A simple
example of an instantiation policy for the sample config is:
"OR('DEFAULT.member', 'DEFAULT.admin')"

Change-Id: I975de7dfa8cc28816b2ea270900b08abb51bb9ef
Signed-off-by: Matthias Neugschwandtner <eug@zurich.ibm.com>
  • Loading branch information
Matthias Neugschwandtner committed Apr 25, 2017
1 parent 8ce1073 commit bb071c5
Show file tree
Hide file tree
Showing 4 changed files with 387 additions and 2 deletions.
3 changes: 3 additions & 0 deletions core/common/ccprovider/ccprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ type ChaincodeData struct {
//This is not currently used anywhere but serves as a good
//eyecatcher
Id []byte `protobuf:"bytes,7,opt,name=id,proto3"`

//InstantiationPolicy for the chaincode
InstantiationPolicy []byte `protobuf:"bytes,8,opt,name=instantiation_policy,proto3"`
}

//implement functions needed from proto.Message for proto's mar/unmarshal functions
Expand Down
8 changes: 8 additions & 0 deletions core/common/ccprovider/sigcdspackage.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ func (ccpack *SignedCDSPackage) GetDepSpec() *pb.ChaincodeDeploymentSpec {
return ccpack.depSpec
}

// GetInstantiationPolicy gets the instantiation policy from the package
func (ccpack *SignedCDSPackage) GetInstantiationPolicy() []byte {
if ccpack.sDepSpec == nil {
panic("GetInstantiationPolicy called on uninitialized package")
}
return ccpack.sDepSpec.InstantiationPolicy
}

// GetDepSpecBytes gets the serialized ChaincodeDeploymentSpec from the package
func (ccpack *SignedCDSPackage) GetDepSpecBytes() []byte {
//this has to be after creating a package and initializing it
Expand Down
98 changes: 98 additions & 0 deletions core/scc/lscc/lscc.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import (
"github.com/hyperledger/fabric/core/policy"
"github.com/hyperledger/fabric/core/policyprovider"
"github.com/hyperledger/fabric/msp/mgmt"
mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
"github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric/protos/peer"
"github.com/hyperledger/fabric/protos/utils"
)
Expand Down Expand Up @@ -220,6 +222,13 @@ func (f InvalidCCOnFSError) Error() string {
return fmt.Sprintf("chaincode fingerprint mismatch %s", string(f))
}

//InstantiationPolicyViolatedErr when chaincode instantiation policy has been violated on instantiate or upgrade
type InstantiationPolicyViolatedErr string

func (f InstantiationPolicyViolatedErr) Error() string {
return "chaincode instantiation policy violated"
}

//-------------- helper functions ------------------
//create the chaincode on the given chain
func (lscc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData) error {
Expand Down Expand Up @@ -462,6 +471,63 @@ func (lscc *LifeCycleSysCC) executeInstall(stub shim.ChaincodeStubInterface, ccb
return err
}

// getInstantiationPolicy retrieves the instantiation policy from a SignedCDSPackage
func (lscc *LifeCycleSysCC) getInstantiationPolicy(stub shim.ChaincodeStubInterface, ccpack ccprovider.CCPackage) ([]byte, error) {
//if ccpack is a SignedCDSPackage, evaluate submitter against instantiation policy
sccpack, isSccpack := ccpack.(*ccprovider.SignedCDSPackage)
if isSccpack {
ip := sccpack.GetInstantiationPolicy()
if ip == nil {
return nil, fmt.Errorf("Instantiation policy cannot be null for a SignedCCDeploymentSpec")
}
return ip, nil
}
return nil, nil
}

// checkInstantiationPolicy evaluates an instantiation policy against a signed proposal
func (lscc *LifeCycleSysCC) checkInstantiationPolicy(stub shim.ChaincodeStubInterface, chainName string, instantiationPolicy []byte) error {
// create a policy object from the policy bytes
mgr := mspmgmt.GetManagerForChain(chainName)
if mgr == nil {
return fmt.Errorf("Error checking chaincode instantiation policy: MSP manager for chain %s not found", chainName)
}
npp := cauthdsl.NewPolicyProvider(mgr)
instPol, _, err := npp.NewPolicy(instantiationPolicy)
if err != nil {
return err
}
// get the signed instantiation proposal
signedProp, err := stub.GetSignedProposal()
if err != nil {
return err
}
proposal, err := utils.GetProposal(signedProp.ProposalBytes)
if err != nil {
return err
}
// get the signature header of the proposal
header, err := utils.GetHeader(proposal.Header)
if err != nil {
return err
}
shdr, err := utils.GetSignatureHeader(header.SignatureHeader)
if err != nil {
return err
}
// construct signed data we can evaluate the instantiation policy against
sd := []*common.SignedData{&common.SignedData{
Data: signedProp.ProposalBytes,
Identity: shdr.Creator,
Signature: signedProp.Signature,
}}
err = instPol.Evaluate(sd)
if err != nil {
return InstantiationPolicyViolatedErr("")
}
return nil
}

// executeDeploy implements the "instantiate" Invoke transaction
func (lscc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, depSpec []byte, policy []byte, escc []byte, vscc []byte) (*ccprovider.ChaincodeData, error) {
cds, err := utils.GetChaincodeDeploymentSpec(depSpec)
Expand Down Expand Up @@ -502,6 +568,18 @@ func (lscc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chai
cd.Vscc = string(vscc)
cd.Policy = policy

// retrieve and evaluate instantiation policy
cd.InstantiationPolicy, err = lscc.getInstantiationPolicy(stub, ccpack)
if err != nil {
return nil, err
}
if cd.InstantiationPolicy != nil {
err = lscc.checkInstantiationPolicy(stub, chainname, cd.InstantiationPolicy)
if err != nil {
return nil, err
}
}

err = lscc.createChaincode(stub, cd)

return cd, err
Expand Down Expand Up @@ -546,6 +624,14 @@ func (lscc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha
return nil, IdenticalVersionErr(cds.ChaincodeSpec.ChaincodeId.Name)
}

//do not upgrade if instantiation policy is violated
if cd.InstantiationPolicy != nil {
err = lscc.checkInstantiationPolicy(stub, chainName, cd.InstantiationPolicy)
if err != nil {
return nil, err
}
}

ccpack, err := ccprovider.GetChaincodeFromFS(chaincodeName, cds.ChaincodeSpec.ChaincodeId.Version)
if err != nil {
return nil, fmt.Errorf("cannot get package for the chaincode to be upgraded (%s:%s)-%s", chaincodeName, cds.ChaincodeSpec.ChaincodeId.Version, err)
Expand All @@ -559,6 +645,18 @@ func (lscc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha
cd.Vscc = string(vscc)
cd.Policy = policy

// retrieve and evaluate new instantiation policy
cd.InstantiationPolicy, err = lscc.getInstantiationPolicy(stub, ccpack)
if err != nil {
return nil, err
}
if cd.InstantiationPolicy != nil {
err = lscc.checkInstantiationPolicy(stub, chainName, cd.InstantiationPolicy)
if err != nil {
return nil, err
}
}

err = lscc.upgradeChaincode(stub, cd)
if err != nil {
return nil, err
Expand Down
Loading

0 comments on commit bb071c5

Please sign in to comment.