diff --git a/core/chaincode/ccproviderimpl.go b/core/chaincode/ccproviderimpl.go index 708c9ae4c5d..824242c051f 100644 --- a/core/chaincode/ccproviderimpl.go +++ b/core/chaincode/ccproviderimpl.go @@ -19,6 +19,8 @@ package chaincode import ( "context" + "fmt" + "github.com/hyperledger/fabric/core/common/ccprovider" "github.com/hyperledger/fabric/core/ledger" pb "github.com/hyperledger/fabric/protos/peer" @@ -72,18 +74,23 @@ func (c *ccProviderImpl) GetCCContext(cid, name, version, txid string, syscc boo // GetCCValidationInfoFromLCCC returns the VSCC and the policy listed in LCCC for the supplied chaincode func (c *ccProviderImpl) GetCCValidationInfoFromLCCC(ctxt context.Context, txid string, prop *pb.Proposal, chainID string, chaincodeID string) (string, []byte, error) { + // LCCC does not have any notion about its own + // endorsing policy - we should never call this + // function with lccc as the chaincodeID + if chaincodeID == "lccc" { + panic("GetCCValidationInfoFromLCCC invoke for LCCC") + } + data, err := GetChaincodeDataFromLCCC(ctxt, txid, prop, chainID, chaincodeID) if err != nil { return "", nil, err } - vscc := "vscc" - // Check whenever VSCC defined for chaincode data - if data != nil && data.Vscc != "" { - vscc = data.Vscc + if data == nil || data.Vscc == "" || data.Policy == nil { + return "", nil, fmt.Errorf("Incorrect validation info in LCCC") } - return vscc, data.Policy, nil + return data.Vscc, data.Policy, nil } // ExecuteChaincode executes the chaincode specified in the context with the specified arguments diff --git a/core/chaincode/exectransaction_test.go b/core/chaincode/exectransaction_test.go index b24dafe0853..d6ac21aea35 100644 --- a/core/chaincode/exectransaction_test.go +++ b/core/chaincode/exectransaction_test.go @@ -141,7 +141,7 @@ func endTxSimulationCDS(chainID string, txid string, txsim ledger.TxSimulator, p return err } // get a proposal - we need it to get a transaction - prop, err := putils.CreateDeployProposalFromCDS(txid, chainID, cds, ss) + prop, err := putils.CreateDeployProposalFromCDS(txid, chainID, cds, ss, nil, nil, nil) if err != nil { return err } diff --git a/core/committer/txvalidator/validator.go b/core/committer/txvalidator/validator.go index 72241b42ad0..c13834f6ad6 100644 --- a/core/committer/txvalidator/validator.go +++ b/core/committer/txvalidator/validator.go @@ -20,7 +20,6 @@ import ( "fmt" "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric/common/cauthdsl" coreUtil "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/common/ccprovider" @@ -28,10 +27,10 @@ import ( "github.com/hyperledger/fabric/core/ledger" ledgerUtil "github.com/hyperledger/fabric/core/ledger/util" "github.com/hyperledger/fabric/msp" + "github.com/hyperledger/fabric/protos/common" "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" - "github.com/syndtr/goleveldb/leveldb/errors" ) // Support provides all of the needed to evaluate the VSCC @@ -177,41 +176,6 @@ func (v *txValidator) Validate(block *common.Block) error { return nil } -// getSemiHardcodedPolicy returns a policy that requests -// one valid signature from the first MSP in this -// chain's MSP manager -// FIXME: this needs to be removed as soon as we extract the policy from LCCC -func getSemiHardcodedPolicy(chainID string, mspMgr msp.MSPManager) ([]byte, error) { - // 1) determine the MSP identifier for the first MSP in this chain - var msp msp.MSP - msps, err := mspMgr.GetMSPs() - if err != nil { - return nil, fmt.Errorf("Could not retrieve the MSPs for the chain manager, err %s", err) - } - if len(msps) == 0 { - return nil, errors.New("At least one MSP was expected") - } - for _, m := range msps { - msp = m - break - } - mspid, err := msp.GetIdentifier() - if err != nil { - return nil, fmt.Errorf("Failure getting the msp identifier, err %s", err) - } - - // 2) get the policy - p := cauthdsl.SignedByMspMember(mspid) - - // 3) marshal it and return it - b, err := proto.Marshal(p) - if err != nil { - return nil, fmt.Errorf("Could not marshal policy, err %s", err) - } - - return b, err -} - func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []byte) error { // Chain ID chainID := payload.Header.ChainHeader.ChainID @@ -230,21 +194,6 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b return err } - // TODO: temporary workaround until the policy is specified - // by the deployer and can be retrieved via LCCC: we create - // a policy that requests 1 valid signature from this chain's - // MSP - policy, err := getSemiHardcodedPolicy(chainID, v.support.MSPManager()) - if err != nil { - return err - } - - // build arguments for VSCC invocation - // args[0] - function name (not used now) - // args[1] - serialized Envelope - // args[2] - serialized policy - args := [][]byte{[]byte(""), envBytes, policy} - ctxt, err := v.ccprovider.GetContext(v.support.Ledger()) if err != nil { logger.Errorf("Cannot obtain context for txid=%s, err %s", txid, err) @@ -258,22 +207,32 @@ func (v *vsccValidatorImpl) VSCCValidateTx(payload *common.Payload, envBytes []b return err } - // TODO: Temporary solution until FAB-1422 get resolved - // Explanation: we actually deploying chaincode transaction, - // hence no lccc yet to query for the data, therefore currently - // introducing a workaround to skip obtaining LCCC data. - vscc := "vscc" - if hdrExt.ChaincodeID.Name != "lccc" { - // Extracting vscc from lccc - // TODO: extract policy as well when available; it's the second argument returned by GetCCValidationInfoFromLCCC - vscc, _, err = v.ccprovider.GetCCValidationInfoFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name) - if err != nil { - logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err) - return err - } + // LCCC should not undergo standard VSCC type of + // validation. It should instead go through system + // policy validation to determine whether the issuer + // is entitled to deploy a chaincode on our chain + // VSCCValidateTx should + if hdrExt.ChaincodeID.Name == "lccc" { + // TODO: until FAB-1934 is in, we need to stop here + logger.Infof("Invocation of LCCC detected, no further VSCC validation necessary") + return nil + } + + // obtain name of the VSCC and the policy from LCCC + vscc, policy, err := v.ccprovider.GetCCValidationInfoFromLCCC(ctxt, txid, nil, chainID, hdrExt.ChaincodeID.Name) + if err != nil { + logger.Errorf("Unable to get chaincode data from LCCC for txid %s, due to %s", txid, err) + return err } + // build arguments for VSCC invocation + // args[0] - function name (not used now) + // args[1] - serialized Envelope + // args[2] - serialized policy + args := [][]byte{[]byte(""), envBytes, policy} + vscctxid := coreUtil.GenerateUUID() + // Get chaincode version version := coreUtil.GetSysCCVersion() cccid := v.ccprovider.GetCCContext(chainID, vscc, version, vscctxid, true, nil) diff --git a/core/common/sysccprovider/sysccprovider.go b/core/common/sysccprovider/sysccprovider.go new file mode 100644 index 00000000000..4e4ec22f5f5 --- /dev/null +++ b/core/common/sysccprovider/sysccprovider.go @@ -0,0 +1,50 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sysccprovider + +// SystemChaincodeProvider provides an abstraction layer that is +// used for different packages to interact with code in the +// system chaincode package without importing it; more methods +// should be added below if necessary +type SystemChaincodeProvider interface { + // IsSysCC returns true if the supplied chaincode is a system chaincode + IsSysCC(name string) bool +} + +var sccFactory SystemChaincodeProviderFactory + +// SystemChaincodeProviderFactory defines a factory interface so +// that the actual implementation can be injected +type SystemChaincodeProviderFactory interface { + NewSystemChaincodeProvider() SystemChaincodeProvider +} + +// RegisterSystemChaincodeProviderFactory is to be called once to set +// the factory that will be used to obtain instances of ChaincodeProvider +func RegisterSystemChaincodeProviderFactory(sccfact SystemChaincodeProviderFactory) { + sccFactory = sccfact +} + +// GetSystemChaincodeProvider returns instances of SystemChaincodeProvider; +// the actual implementation is controlled by the factory that +// is registered via RegisterSystemChaincodeProviderFactory +func GetSystemChaincodeProvider() SystemChaincodeProvider { + if sccFactory == nil { + panic("The factory must be set first via RegisterSystemChaincodeProviderFactory") + } + return sccFactory.NewSystemChaincodeProvider() +} diff --git a/core/endorser/endorser.go b/core/endorser/endorser.go index e9cd2b85dc8..e7daf0ec040 100644 --- a/core/endorser/endorser.go +++ b/core/endorser/endorser.go @@ -128,7 +128,7 @@ func (e *Endorser) callChaincode(ctxt context.Context, chainID string, version s // //NOTE that if there's an error all simulation, including the chaincode //table changes in lccc will be thrown away - if cid.Name == "lccc" && len(cis.ChaincodeSpec.Input.Args) == 3 && (string(cis.ChaincodeSpec.Input.Args[0]) == "deploy" || string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade") { + if cid.Name == "lccc" && len(cis.ChaincodeSpec.Input.Args) >= 3 && (string(cis.ChaincodeSpec.Input.Args[0]) == "deploy" || string(cis.ChaincodeSpec.Input.Args[0]) == "upgrade") { var ccVersion string switch string(cis.ChaincodeSpec.Input.Args[0]) { case "deploy": diff --git a/core/peer/peer.go b/core/peer/peer.go index 5f36cefe765..1ced444b409 100644 --- a/core/peer/peer.go +++ b/core/peer/peer.go @@ -34,6 +34,7 @@ import ( "github.com/hyperledger/fabric/core/ledger" "github.com/hyperledger/fabric/core/ledger/ledgermgmt" "github.com/hyperledger/fabric/gossip/service" + "github.com/hyperledger/fabric/msp" mspmgmt "github.com/hyperledger/fabric/msp/mgmt" "github.com/hyperledger/fabric/peer/sharedconfig" "github.com/hyperledger/fabric/protos/common" @@ -255,6 +256,17 @@ func GetLedger(cid string) ledger.PeerLedger { return nil } +// GetMSPMgr returns the MSP manager of the chain with chain ID. +// Note that this call returns nil if chain cid has not been created. +func GetMSPMgr(cid string) msp.MSPManager { + chains.RLock() + defer chains.RUnlock() + if c, ok := chains.list[cid]; ok { + return c.cs.MSPManager() + } + return nil +} + // GetCommitter returns the committer of the chain with chain ID. Note that this // call returns nil if chain cid has not been created. func GetCommitter(cid string) committer.Committer { diff --git a/core/scc/lccc/lccc.go b/core/scc/lccc/lccc.go index 225fab7f150..821d455c3a4 100644 --- a/core/scc/lccc/lccc.go +++ b/core/scc/lccc/lccc.go @@ -22,9 +22,12 @@ import ( "strings" "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/common/cauthdsl" "github.com/hyperledger/fabric/core/chaincode/shim" "github.com/hyperledger/fabric/core/common/ccprovider" + "github.com/hyperledger/fabric/core/common/sysccprovider" pb "github.com/hyperledger/fabric/protos/peer" + "github.com/hyperledger/fabric/protos/utils" "github.com/op/go-logging" ) @@ -69,6 +72,10 @@ const ( // LifeCycleSysCC implements chaincode lifecycle and policies aroud it type LifeCycleSysCC struct { + // sccprovider is the interface with which we call + // methods of the system chaincode package without + // import cycles + sccprovider sysccprovider.SystemChaincodeProvider } //----------------errors--------------- @@ -159,18 +166,26 @@ func (m MarshallErr) Error() string { //-------------- helper functions ------------------ //create the chaincode on the given chain -func (lccc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, cccode []byte) (*ccprovider.ChaincodeData, error) { - return lccc.putChaincodeData(stub, chainname, ccname, startVersion, cccode) +func (lccc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, cccode []byte, policy []byte, escc []byte, vscc []byte) (*ccprovider.ChaincodeData, error) { + return lccc.putChaincodeData(stub, chainname, ccname, startVersion, cccode, policy, escc, vscc) } //upgrade the chaincode on the given chain -func (lccc *LifeCycleSysCC) upgradeChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, version string, cccode []byte) (*ccprovider.ChaincodeData, error) { - return lccc.putChaincodeData(stub, chainname, ccname, version, cccode) +func (lccc *LifeCycleSysCC) upgradeChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, version string, cccode []byte, policy []byte, escc []byte, vscc []byte) (*ccprovider.ChaincodeData, error) { + return lccc.putChaincodeData(stub, chainname, ccname, version, cccode, policy, escc, vscc) } //create the chaincode on the given chain -func (lccc *LifeCycleSysCC) putChaincodeData(stub shim.ChaincodeStubInterface, chainname string, ccname string, version string, cccode []byte) (*ccprovider.ChaincodeData, error) { - cd := &ccprovider.ChaincodeData{Name: ccname, Version: version, DepSpec: cccode} +func (lccc *LifeCycleSysCC) putChaincodeData(stub shim.ChaincodeStubInterface, chainname string, ccname string, version string, cccode []byte, policy []byte, escc []byte, vscc []byte) (*ccprovider.ChaincodeData, error) { + // check that escc and vscc are real system chaincodes + if !lccc.sccprovider.IsSysCC(string(escc)) { + return nil, fmt.Errorf("%s is not a valid endorsement system chaincode", string(escc)) + } + if !lccc.sccprovider.IsSysCC(string(vscc)) { + return nil, fmt.Errorf("%s is not a valid validation system chaincode", string(vscc)) + } + + cd := &ccprovider.ChaincodeData{Name: ccname, Version: version, DepSpec: cccode, Policy: policy, Escc: string(escc), Vscc: string(vscc)} cdbytes, err := proto.Marshal(cd) if err != nil { return nil, err @@ -247,7 +262,7 @@ func (lccc *LifeCycleSysCC) isValidChaincodeName(chaincodename string) bool { } //this implements "deploy" Invoke transaction -func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, code []byte) error { +func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, code []byte, policy []byte, escc []byte, vscc []byte) error { cds, err := lccc.getChaincodeDeploymentSpec(code) if err != nil { @@ -267,13 +282,13 @@ func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chai return ExistsErr(cds.ChaincodeSpec.ChaincodeID.Name) } - _, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, code) + _, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, code, policy, escc, vscc) return err } //this implements "upgrade" Invoke transaction -func (lccc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, chainName string, code []byte) ([]byte, error) { +func (lccc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, chainName string, code []byte, policy []byte, escc []byte, vscc []byte) ([]byte, error) { cds, err := lccc.getChaincodeDeploymentSpec(code) if err != nil { return nil, err @@ -312,7 +327,7 @@ func (lccc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha // replace the ChaincodeDeploymentSpec using the next version newVersion := fmt.Sprintf("%d", (v + 1)) - newCD, err := lccc.upgradeChaincode(stub, chainName, chaincodeName, newVersion, code) + newCD, err := lccc.upgradeChaincode(stub, chainName, chaincodeName, newVersion, code, policy, escc, vscc) if err != nil { return nil, err } @@ -322,8 +337,9 @@ func (lccc *LifeCycleSysCC) executeUpgrade(stub shim.ChaincodeStubInterface, cha //-------------- the chaincode stub interface implementation ---------- -//Init does nothing +//Init only initializes the system chaincode provider func (lccc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response { + lccc.sccprovider = sysccprovider.GetSystemChaincodeProvider() return shim.Success(nil) } @@ -342,7 +358,7 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response switch function { case DEPLOY: - if len(args) != 3 { + if len(args) < 3 || len(args) > 6 { return shim.Error(InvalidArgsLenErr(len(args)).Error()) } @@ -357,13 +373,39 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response //bytes corresponding to deployment spec code := args[2] - err := lccc.executeDeploy(stub, chainname, code) + // optional arguments here (they can each be nil and may or may not be present) + // args[3] is a marshalled SignaturePolicyEnvelope representing the endorsement policy + // args[4] is the name of escc + // args[5] is the name of vscc + var policy []byte + if len(args) > 3 && args[3] != nil { + policy = args[3] + } else { + // FIXME: temporary workaround until all SDKs provide a policy + policy = utils.MarshalOrPanic(cauthdsl.SignedByMspMember("DEFAULT")) + } + + var escc []byte + if len(args) > 4 && args[4] != nil { + escc = args[4] + } else { + escc = []byte("escc") + } + + var vscc []byte + if len(args) > 5 && args[5] != nil { + vscc = args[5] + } else { + vscc = []byte("vscc") + } + + err := lccc.executeDeploy(stub, chainname, code, policy, escc, vscc) if err != nil { return shim.Error(err.Error()) } return shim.Success(nil) case UPGRADE: - if len(args) != 3 { + if len(args) < 3 || len(args) > 6 { return shim.Error(InvalidArgsLenErr(len(args)).Error()) } @@ -373,7 +415,34 @@ func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response } code := args[2] - verBytes, err := lccc.executeUpgrade(stub, chainname, code) + + // optional arguments here (they can each be nil and may or may not be present) + // args[3] is a marshalled SignaturePolicyEnvelope representing the endorsement policy + // args[4] is the name of escc + // args[5] is the name of vscc + var policy []byte + if len(args) > 3 && args[3] != nil { + policy = args[3] + } else { + // FIXME: temporary workaround until all SDKs provide a policy + policy = utils.MarshalOrPanic(cauthdsl.SignedByMspMember("DEFAULT")) + } + + var escc []byte + if len(args) > 4 && args[4] != nil { + escc = args[4] + } else { + escc = []byte("escc") + } + + var vscc []byte + if len(args) > 5 && args[5] != nil { + vscc = args[5] + } else { + vscc = []byte("vscc") + } + + verBytes, err := lccc.executeUpgrade(stub, chainname, code, policy, escc, vscc) if err != nil { return shim.Error(err.Error()) } diff --git a/core/scc/lccc/lccc_test.go b/core/scc/lccc/lccc_test.go index 6d5e49a0e9a..6e00a7215b0 100644 --- a/core/scc/lccc/lccc_test.go +++ b/core/scc/lccc/lccc_test.go @@ -19,12 +19,29 @@ import ( "fmt" "testing" + "os" + "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/hyperledger/fabric/core/common/sysccprovider" "github.com/hyperledger/fabric/core/container" pb "github.com/hyperledger/fabric/protos/peer" ) +type mocksccProviderFactory struct { +} + +func (c *mocksccProviderFactory) NewSystemChaincodeProvider() sysccprovider.SystemChaincodeProvider { + return &mocksccProviderImpl{} +} + +type mocksccProviderImpl struct { +} + +func (c *mocksccProviderImpl) IsSysCC(name string) bool { + return true +} + func register(stub *shim.MockStub, ccname string) error { args := [][]byte{[]byte("register"), []byte(ccname)} if res := stub.MockInvoke("1", args); res.Status != shim.OK { @@ -48,6 +65,11 @@ func TestDeploy(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte if b, err = proto.Marshal(cds); err != nil || b == nil { @@ -65,6 +87,11 @@ func TestInvalidCodeDeploy(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + baddepspec := []byte("bad deploy spec") args := [][]byte{[]byte(DEPLOY), []byte("test"), baddepspec} res := stub.MockInvoke("1", args) @@ -80,6 +107,11 @@ func TestInvalidChaincodeName(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) //change name to empty @@ -103,6 +135,11 @@ func TestRedeploy(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte if b, err = proto.Marshal(cds); err != nil || b == nil { @@ -127,6 +164,11 @@ func TestCheckCC(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte if b, err = proto.Marshal(cds); err != nil || b == nil { @@ -149,6 +191,11 @@ func TestMultipleDeploy(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + //deploy 02 cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte @@ -188,6 +235,11 @@ func TestRetryFailedDeploy(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + //deploy 02 cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte @@ -226,6 +278,11 @@ func TestUpgrade(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte if b, err = proto.Marshal(cds); err != nil || b == nil { @@ -261,6 +318,11 @@ func TestUpgradeNonExistChaincode(t *testing.T) { scc := new(LifeCycleSysCC) stub := shim.NewMockStub("lccc", scc) + if res := stub.MockInit("1", nil); res.Status != shim.OK { + fmt.Println("Init failed", string(res.Message)) + t.FailNow() + } + cds, err := constructDeploymentSpec("example02", "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example02", [][]byte{[]byte("init"), []byte("a"), []byte("100"), []byte("b"), []byte("200")}) var b []byte if b, err = proto.Marshal(cds); err != nil || b == nil { @@ -284,3 +346,8 @@ func TestUpgradeNonExistChaincode(t *testing.T) { t.FailNow() } } + +func TestMain(m *testing.M) { + sysccprovider.RegisterSystemChaincodeProviderFactory(&mocksccProviderFactory{}) + os.Exit(m.Run()) +} diff --git a/core/scc/sccproviderimpl.go b/core/scc/sccproviderimpl.go new file mode 100644 index 00000000000..cc546997852 --- /dev/null +++ b/core/scc/sccproviderimpl.go @@ -0,0 +1,46 @@ +/* +Copyright IBM Corp. 2017 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package scc + +import ( + "github.com/hyperledger/fabric/core/common/sysccprovider" +) + +// sccProviderFactory implements the sysccprovider.SystemChaincodeProviderFactory +// interface and returns instances of sysccprovider.SystemChaincodeProvider +type sccProviderFactory struct { +} + +// NewSystemChaincodeProvider returns pointers to sccProviderFactory as an +// implementer of the sysccprovider.SystemChaincodeProvider interface +func (c *sccProviderFactory) NewSystemChaincodeProvider() sysccprovider.SystemChaincodeProvider { + return &sccProviderImpl{} +} + +// init is called when this package is loaded. This implementation registers the factory +func init() { + sysccprovider.RegisterSystemChaincodeProviderFactory(&sccProviderFactory{}) +} + +// ccProviderImpl is an implementation of the ccprovider.ChaincodeProvider interface +type sccProviderImpl struct { +} + +// IsSysCC returns true if the supplied chaincode is a system chaincode +func (c *sccProviderImpl) IsSysCC(name string) bool { + return IsSysCC(name) +} diff --git a/peer/chaincode/chaincode.go b/peer/chaincode/chaincode.go index 29e48046f54..e54e1f8fafb 100644 --- a/peer/chaincode/chaincode.go +++ b/peer/chaincode/chaincode.go @@ -48,6 +48,12 @@ func AddFlags(cmd *cobra.Command) { fmt.Sprint("Name of a custom ID generation algorithm (hashing and decoding) e.g. sha256base64")) flags.StringVarP(&chainID, "chainID", "C", util.GetTestChainID(), fmt.Sprint("The chain on which this command should be executed")) + flags.StringVarP(&policy, "policy", "P", common.UndefinedParamValue, + fmt.Sprint("The endorsement policy associated to this chaincode")) + flags.StringVarP(&escc, "escc", "E", common.UndefinedParamValue, + fmt.Sprint("The name of the endorsement system chaincode to be used for this chaincode")) + flags.StringVarP(&vscc, "vscc", "V", common.UndefinedParamValue, + fmt.Sprint("The name of the verification system chaincode to be used for this chaincode")) } // Cmd returns the cobra command for Chaincode @@ -73,6 +79,10 @@ var ( chaincodeQueryHex bool customIDGenAlg string chainID string + policy string + escc string + vscc string + policyMarhsalled []byte ) var chaincodeCmd = &cobra.Command{ diff --git a/peer/chaincode/common.go b/peer/chaincode/common.go index 8d080b91d87..a135abeb3ef 100644 --- a/peer/chaincode/common.go +++ b/peer/chaincode/common.go @@ -23,6 +23,7 @@ import ( "os" "strings" + "github.com/hyperledger/fabric/common/cauthdsl" cutil "github.com/hyperledger/fabric/common/util" "github.com/hyperledger/fabric/core/chaincode" "github.com/hyperledger/fabric/core/chaincode/platforms" @@ -133,6 +134,47 @@ func checkChaincodeCmdParams(cmd *cobra.Command) error { return fmt.Errorf("Must supply value for %s name parameter.\n", chainFuncName) } + // if it's not a deploy or an upgrade we don't need policy, escc and vscc + if cmd.Name() != deploy_cmdname && cmd.Name() != upgrade_cmdname { + if escc != common.UndefinedParamValue { + return fmt.Errorf("escc should be supplied only to chaincode deploy requests") + } + + if vscc != common.UndefinedParamValue { + return fmt.Errorf("vscc should be supplied only to chaincode deploy requests") + } + + if policy != common.UndefinedParamValue { + return fmt.Errorf("policy should be supplied only to chaincode deploy requests") + } + } else { + if escc != common.UndefinedParamValue { + logger.Infof("Using escc %s", escc) + } else { + logger.Infof("Using default escc") + escc = "escc" + } + + if vscc != common.UndefinedParamValue { + logger.Infof("Using vscc %s", vscc) + } else { + logger.Infof("Using default vscc") + vscc = "vscc" + } + + if policy != common.UndefinedParamValue { + p, err := cauthdsl.FromString(policy) + if err != nil { + return fmt.Errorf("Invalid policy %s\n", policy) + } + policyMarhsalled = putils.MarshalOrPanic(p) + } else { + // FIXME: we need to get the default from somewhere + p := cauthdsl.SignedByMspMember("DEFAULT") + policyMarhsalled = putils.MarshalOrPanic(p) + } + } + // Check that non-empty chaincode parameters contain only Args as a key. // Type checking is done later when the JSON is actually unmarshaled // into a pb.ChaincodeInput. To better understand what's going diff --git a/peer/chaincode/common_test.go b/peer/chaincode/common_test.go index a266afa14da..5f2730f521e 100644 --- a/peer/chaincode/common_test.go +++ b/peer/chaincode/common_test.go @@ -21,6 +21,7 @@ import ( "testing" pb "github.com/hyperledger/fabric/protos/peer" + "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) @@ -29,7 +30,7 @@ func TestCheckChaincodeCmdParamsWithNewCallingSchema(t *testing.T) { chaincodePath = "some/path" chaincodeName = "somename" require := require.New(t) - result := checkChaincodeCmdParams(nil) + result := checkChaincodeCmdParams(&cobra.Command{}) require.Nil(result) } @@ -39,7 +40,7 @@ func TestCheckChaincodeCmdParamsWithOldCallingSchema(t *testing.T) { chaincodePath = "some/path" chaincodeName = "somename" require := require.New(t) - result := checkChaincodeCmdParams(nil) + result := checkChaincodeCmdParams(&cobra.Command{}) require.Nil(result) } @@ -49,7 +50,7 @@ func TestCheckChaincodeCmdParamsWithoutName(t *testing.T) { chaincodePath = "some/path" chaincodeName = "" require := require.New(t) - result := checkChaincodeCmdParams(nil) + result := checkChaincodeCmdParams(&cobra.Command{}) require.Error(result) } @@ -59,7 +60,7 @@ func TestCheckChaincodeCmdParamsWithFunctionOnly(t *testing.T) { chaincodePath = "some/path" chaincodeName = "somename" require := require.New(t) - result := checkChaincodeCmdParams(nil) + result := checkChaincodeCmdParams(&cobra.Command{}) require.Error(result) } @@ -69,7 +70,7 @@ func TestCheckChaincodeCmdParamsEmptyCtor(t *testing.T) { chaincodePath = "some/path" chaincodeName = "somename" require := require.New(t) - result := checkChaincodeCmdParams(nil) + result := checkChaincodeCmdParams(&cobra.Command{}) require.Error(result) } diff --git a/peer/chaincode/deploy.go b/peer/chaincode/deploy.go index 65579624ba1..7a2e787484a 100644 --- a/peer/chaincode/deploy.go +++ b/peer/chaincode/deploy.go @@ -30,10 +30,12 @@ import ( var chaincodeDeployCmd *cobra.Command +const deploy_cmdname = "deploy" + // deployCmd returns the cobra command for Chaincode Deploy func deployCmd(cf *ChaincodeCmdFactory) *cobra.Command { chaincodeDeployCmd = &cobra.Command{ - Use: "deploy", + Use: deploy_cmdname, Short: fmt.Sprintf("Deploy the specified chaincode to the network."), Long: fmt.Sprintf(`Deploy the specified chaincode to the network.`), ValidArgs: []string{"1"}, @@ -64,7 +66,7 @@ func deploy(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, uuid := util.GenerateUUID() - prop, err := utils.CreateDeployProposalFromCDS(uuid, chainID, cds, creator) + prop, err := utils.CreateDeployProposalFromCDS(uuid, chainID, cds, creator, policyMarhsalled, []byte(escc), []byte(vscc)) if err != nil { return nil, fmt.Errorf("Error creating proposal %s: %s\n", chainFuncName, err) } diff --git a/peer/chaincode/upgrade.go b/peer/chaincode/upgrade.go index 184fd58a59d..614639ab9e3 100644 --- a/peer/chaincode/upgrade.go +++ b/peer/chaincode/upgrade.go @@ -30,10 +30,12 @@ import ( var chaincodeUpgradeCmd *cobra.Command +const upgrade_cmdname = "upgrade" + // upgradeCmd returns the cobra command for Chaincode Upgrade func upgradeCmd(cf *ChaincodeCmdFactory) *cobra.Command { chaincodeUpgradeCmd = &cobra.Command{ - Use: "upgrade", + Use: upgrade_cmdname, Short: fmt.Sprintf("Upgrade chaincode."), Long: fmt.Sprintf(`Upgrade an existing chaincode with the specified one. The new chaincode will immediately replace the existing chaincode upon the transaction committed.`), ValidArgs: []string{"1"}, @@ -64,7 +66,7 @@ func upgrade(cmd *cobra.Command, cf *ChaincodeCmdFactory) (*protcommon.Envelope, uuid := util.GenerateUUID() - prop, err := utils.CreateUpgradeProposalFromCDS(uuid, chainID, cds, creator) + prop, err := utils.CreateUpgradeProposalFromCDS(uuid, chainID, cds, creator, policyMarhsalled, []byte(escc), []byte(vscc)) if err != nil { return nil, fmt.Errorf("Error creating proposal %s: %s\n", chainFuncName, err) } diff --git a/protos/utils/proputils.go b/protos/utils/proputils.go index b5742ddbb72..ee2ba83b440 100644 --- a/protos/utils/proputils.go +++ b/protos/utils/proputils.go @@ -240,6 +240,17 @@ func GetSignatureHeader(bytes []byte) (*common.SignatureHeader, error) { return sh, nil } +// GetSignaturePolicyEnvelope returns a SignaturePolicyEnvelope from bytes +func GetSignaturePolicyEnvelope(bytes []byte) (*common.SignaturePolicyEnvelope, error) { + p := &common.SignaturePolicyEnvelope{} + err := proto.Unmarshal(bytes, p) + if err != nil { + return nil, err + } + + return p, nil +} + // CreateChaincodeProposal creates a proposal from given input func CreateChaincodeProposal(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, error) { return CreateChaincodeProposalWithTransient(txid, typ, chainID, cis, creator, nil) @@ -438,17 +449,17 @@ func CreateProposalFromCIS(txid string, typ common.HeaderType, chainID string, c } // CreateDeployProposalFromCDS returns a deploy proposal given a serialized identity and a ChaincodeDeploymentSpec -func CreateDeployProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte) (*peer.Proposal, error) { - return createProposalFromCDS(txid, chainID, cds, creator, true) +func CreateDeployProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, error) { + return createProposalFromCDS(txid, chainID, cds, creator, policy, escc, vscc, true) } // CreateUpgradeProposalFromCDS returns a upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec -func CreateUpgradeProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte) (*peer.Proposal, error) { - return createProposalFromCDS(txid, chainID, cds, creator, false) +func CreateUpgradeProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, error) { + return createProposalFromCDS(txid, chainID, cds, creator, policy, escc, vscc, false) } // createProposalFromCDS returns a deploy or upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec -func createProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, deploy bool) (*peer.Proposal, error) { +func createProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte, deploy bool) (*peer.Proposal, error) { b, err := proto.Marshal(cds) if err != nil { return nil, err @@ -465,7 +476,7 @@ func createProposalFromCDS(txid string, chainID string, cds *peer.ChaincodeDeplo ChaincodeSpec: &peer.ChaincodeSpec{ Type: peer.ChaincodeSpec_GOLANG, ChaincodeID: &peer.ChaincodeID{Name: "lccc"}, - Input: &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b}}}} + Input: &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b, policy, escc, vscc}}}} //...and get the proposal for it return CreateProposalFromCIS(txid, common.HeaderType_ENDORSER_TRANSACTION, chainID, lcccSpec, creator)