Skip to content

Commit

Permalink
FAB-10997 Add ChaincodeDefinition function to LSCC
Browse files Browse the repository at this point in the history
Currently, the endorser and chaincode package get chaincode definitions
by invoking the LSCC.  This creates a needless round of marshaling and
unmarshaling and other chaincode overhead, plus makes the function
signatures large and hard to read.  This CR adds a new
ChaincodeDefinition method to LSCC which simply accepts a txsim and a
chaincode name.  With these two things, the necessary reads are attached
to the transaction while bypassing the rest of the chaincode overhead.

Change-Id: I1fcf2c9a71f4713bfd391138fc0b1458e84fa32f
Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
  • Loading branch information
Jason Yellick committed Jul 16, 2018
1 parent 0e578a8 commit 8ec2ffa
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 18 deletions.
23 changes: 23 additions & 0 deletions core/scc/lscc/lscc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/hyperledger/fabric/core/common/ccprovider"
"github.com/hyperledger/fabric/core/common/privdata"
"github.com/hyperledger/fabric/core/common/sysccprovider"
"github.com/hyperledger/fabric/core/ledger"
"github.com/hyperledger/fabric/core/ledger/cceventmgmt"
"github.com/hyperledger/fabric/core/peer"
"github.com/hyperledger/fabric/core/policy"
Expand Down Expand Up @@ -170,6 +171,9 @@ func (lscc *LifeCycleSysCC) ChaincodeDeploymentSpec(channelID, ccName string) (*
return nil, errors.Errorf("chaincode %s not found on channel %s", ccName, channelID)
}

// Note, although it looks very tempting to replace the bulk of this function with
// the below 'ChaincodeDefinition' call, the 'getCCCode' call provides us security
// by side-effect, so we must leave it as is for now.
cds, _, err := lscc.getCCCode(ccName, chaincodeDataBytes)
if err != nil {
return nil, errors.Wrapf(err, "could not get chaincode code")
Expand All @@ -178,6 +182,25 @@ func (lscc *LifeCycleSysCC) ChaincodeDeploymentSpec(channelID, ccName string) (*
return cds, nil
}

func (lscc *LifeCycleSysCC) ChaincodeDefinition(chaincodeName string, txsim ledger.QueryExecutor) (ccprovider.ChaincodeDefinition, error) {
chaincodeDataBytes, err := txsim.GetState("lscc", chaincodeName)
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve state for chaincode %s", chaincodeName)
}

if chaincodeDataBytes == nil {
return nil, errors.Errorf("chaincode %s not found", chaincodeName)
}

chaincodeData := &ccprovider.ChaincodeData{}
err = proto.Unmarshal(chaincodeDataBytes, chaincodeData)
if err != nil {
return nil, errors.Wrapf(err, "chaincode %s has bad definition", chaincodeName)
}

return chaincodeData, nil
}

//create the chaincode on the given chain
func (lscc *LifeCycleSysCC) putChaincodeData(stub shim.ChaincodeStubInterface, cd *ccprovider.ChaincodeData) error {
cdbytes, err := proto.Marshal(cd)
Expand Down
95 changes: 77 additions & 18 deletions core/scc/lscc/lscc_noncc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@ import (
var _ = Describe("LSCC", func() {

var (
l *lscc.LifeCycleSysCC
fakeSupport *mock.FileSystemSupport
fakeSCCProvider *mock.SystemChaincodeProvider
l *lscc.LifeCycleSysCC
fakeSupport *mock.FileSystemSupport
fakeSCCProvider *mock.SystemChaincodeProvider
fakeQueryExecutor *mock.QueryExecutor
ccData *ccprovider.ChaincodeData
ccDataBytes []byte
err error
)

BeforeEach(func() {
Expand All @@ -36,28 +40,32 @@ var _ = Describe("LSCC", func() {
Support: fakeSupport,
SCCProvider: fakeSCCProvider,
}

ccData = &ccprovider.ChaincodeData{
Name: "chaincode-data-name",
Version: "version",
Escc: "escc",
Vscc: "vscc",
Policy: []byte("policy"),
Data: []byte("data"),
Id: []byte("id"),
InstantiationPolicy: []byte("instantiation-policy"),
}

ccDataBytes, err = proto.Marshal(ccData)
Expect(err).NotTo(HaveOccurred())

fakeQueryExecutor = &mock.QueryExecutor{}
fakeQueryExecutor.GetStateReturns(ccDataBytes, nil)
})

Describe("GetChaincodeDeploymentSpec", func() {
var (
fakeQueryExecutor *mock.QueryExecutor
ccData *ccprovider.ChaincodeData
ccDataBytes []byte
fakeCCPackage *mock.CCPackage
err error
deploymentSpec *pb.ChaincodeDeploymentSpec
fakeCCPackage *mock.CCPackage
deploymentSpec *pb.ChaincodeDeploymentSpec
)

BeforeEach(func() {
ccData = &ccprovider.ChaincodeData{
Name: "chaincode-data-name",
}
ccDataBytes, err = proto.Marshal(ccData)
Expect(err).NotTo(HaveOccurred())

fakeQueryExecutor = &mock.QueryExecutor{}
fakeQueryExecutor.GetStateReturns(ccDataBytes, nil)

fakeSCCProvider.GetQueryExecutorForLedgerReturns(fakeQueryExecutor, nil)

deploymentSpec = &pb.ChaincodeDeploymentSpec{
Expand Down Expand Up @@ -122,4 +130,55 @@ var _ = Describe("LSCC", func() {
})
})
})

Describe("ChaincodeDefinition", func() {
BeforeEach(func() {
})

It("retrieves the chaincode data from the state", func() {
chaincodeDefinition, err := l.ChaincodeDefinition("cc-name", fakeQueryExecutor)
Expect(err).NotTo(HaveOccurred())
returnedChaincodeData, ok := chaincodeDefinition.(*ccprovider.ChaincodeData)
Expect(ok).To(BeTrue())
Expect(returnedChaincodeData).To(Equal(ccData))

Expect(fakeQueryExecutor.GetStateCallCount()).To(Equal(1))
namespace, key := fakeQueryExecutor.GetStateArgsForCall(0)
Expect(namespace).To(Equal("lscc"))
Expect(key).To(Equal("cc-name"))
})

Context("when the state getter fails", func() {
BeforeEach(func() {
fakeQueryExecutor.GetStateReturns(nil, errors.New("fake-error"))
})

It("returns the wrapped error", func() {
_, err := l.ChaincodeDefinition("cc-name", fakeQueryExecutor)
Expect(err).To(MatchError("could not retrieve state for chaincode cc-name: fake-error"))
})
})

Context("when the state getter does not find the key", func() {
BeforeEach(func() {
fakeQueryExecutor.GetStateReturns(nil, nil)
})

It("returns an error", func() {
_, err := l.ChaincodeDefinition("cc-name", fakeQueryExecutor)
Expect(err).To(MatchError("chaincode cc-name not found"))
})
})

Context("when the state getter returns invalid data", func() {
BeforeEach(func() {
fakeQueryExecutor.GetStateReturns([]byte("garbage"), nil)
})

It("wraps and returns the error", func() {
_, err := l.ChaincodeDefinition("cc-name", fakeQueryExecutor)
Expect(err).To(MatchError(MatchRegexp("chaincode cc-name has bad definition: proto:.*")))
})
})
})
})

0 comments on commit 8ec2ffa

Please sign in to comment.