Skip to content

Commit

Permalink
pvtData APIs are not allowed in chaincode Init()
Browse files Browse the repository at this point in the history
private data APIs are not allowed in Init() since the
private data collection configuration is not yet committed
on the channel.

This CR provide a better error message indicating that
pvtdata APIs are  not allowed in chaincode Init().

FAB-13050 #done

Change-Id: I6e6a8abdf1f35f56307be90d8b6b2830c8f35755
Signed-off-by: senthil <cendhu@gmail.com>
  • Loading branch information
cendhu committed Dec 5, 2018
1 parent f63c95d commit 3e7b2ef
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 8 deletions.
75 changes: 69 additions & 6 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ func getDeployLSCCSpec(chainID string, cds *pb.ChaincodeDeploymentSpec, ccp *com
}

// Deploy a chaincode - i.e., build and initialize.
func deploy(chainID string, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (b []byte, err error) {
func deploy(chainID string, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (resp *pb.Response, err error) {
// First build and get the deployment spec
cdDeploymentSpec, err := getDeploymentSpec(spec)
if err != nil {
Expand All @@ -412,7 +412,7 @@ func deploy(chainID string, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec,
}

func deployWithCollectionConfigs(chainID string, cccid *ccprovider.CCContext, spec *pb.ChaincodeSpec,
collectionConfigPkg *common.CollectionConfigPackage, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (b []byte, err error) {
collectionConfigPkg *common.CollectionConfigPackage, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (resp *pb.Response, err error) {
// First build and get the deployment spec
cdDeploymentSpec, err := getDeploymentSpec(spec)
if err != nil {
Expand All @@ -422,7 +422,7 @@ func deployWithCollectionConfigs(chainID string, cccid *ccprovider.CCContext, sp
}

func deploy2(chainID string, cccid *ccprovider.CCContext, chaincodeDeploymentSpec *pb.ChaincodeDeploymentSpec,
collectionConfigPkg *common.CollectionConfigPackage, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (b []byte, err error) {
collectionConfigPkg *common.CollectionConfigPackage, blockNumber uint64, chaincodeSupport *ChaincodeSupport) (resp *pb.Response, err error) {
cis, err := getDeployLSCCSpec(chainID, chaincodeDeploymentSpec, collectionConfigPkg)
if err != nil {
return nil, fmt.Errorf("Error creating lscc spec : %s\n", err)
Expand Down Expand Up @@ -468,12 +468,11 @@ func deploy2(chainID string, cccid *ccprovider.CCContext, chaincodeDeploymentSpe
return nil, fmt.Errorf("Error deploying chaincode (1): %s", err)
}

var resp *pb.Response
if resp, _, err = chaincodeSupport.ExecuteLegacyInit(txParams, cccid, chaincodeDeploymentSpec); err != nil {
return nil, fmt.Errorf("Error deploying chaincode(2): %s", err)
}

return resp.Payload, nil
return resp, nil
}

// Invoke a chaincode.
Expand Down Expand Up @@ -965,6 +964,70 @@ func TestChaincodeInvokeChaincodeErrorCase(t *testing.T) {
}
}

func TestChaincodeInit(t *testing.T) {
chainID := util.GetTestChainID()

_, chaincodeSupport, cleanup, err := initPeer(chainID)
if err != nil {
t.Fail()
t.Logf("Error creating peer: %s", err)
}

defer cleanup()

url := "github.com/hyperledger/fabric/core/chaincode/testdata/chaincode/init_private_data"
cID := &pb.ChaincodeID{Name: "init_pvtdata", Path: url, Version: "0"}

f := "init"
args := util.ToChaincodeArgs(f)

spec := &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}

cccid := &ccprovider.CCContext{
Name: "tmap",
Version: "0",
}

defer chaincodeSupport.Stop(&ccprovider.ChaincodeContainerInfo{
Name: cID.Name,
Version: cID.Version,
Path: cID.Path,
Type: "GOLANG",
ContainerType: "DOCKER",
})

var nextBlockNumber uint64 = 1
_, err = deploy(chainID, cccid, spec, nextBlockNumber, chaincodeSupport)
assert.Contains(t, err.Error(), "private data APIs are not allowed in chaincode Init")

url = "github.com/hyperledger/fabric/core/chaincode/testdata/chaincode/init_public_data"
cID = &pb.ChaincodeID{Name: "init_public_data", Path: url, Version: "0"}

f = "init"
args = util.ToChaincodeArgs(f)

spec = &pb.ChaincodeSpec{Type: 1, ChaincodeId: cID, Input: &pb.ChaincodeInput{Args: args}}

cccid = &ccprovider.CCContext{
Name: "tmap",
Version: "0",
}

defer chaincodeSupport.Stop(&ccprovider.ChaincodeContainerInfo{
Name: cID.Name,
Version: cID.Version,
Path: cID.Path,
Type: "GOLANG",
ContainerType: "DOCKER",
})

resp, err := deploy(chainID, cccid, spec, nextBlockNumber, chaincodeSupport)
assert.NoError(t, err)
// why response status is defined as int32 when the status codes are
// defined as int (i.e., constant)
assert.Equal(t, int32(shim.OK), resp.Status)
}

// Test the invocation of a transaction.
func TestQueries(t *testing.T) {
// Allow queries test alone so that end to end test can be performed. It takes less than 5 seconds.
Expand Down Expand Up @@ -1431,7 +1494,7 @@ func setupTestConfig() {
}
}

func deployChaincode(ctx context.Context, name string, version string, chaincodeType pb.ChaincodeSpec_Type, path string, args [][]byte, creator []byte, channel string, nextBlockNumber uint64, chaincodeSupport *ChaincodeSupport) ([]byte, *ccprovider.CCContext, error) {
func deployChaincode(ctx context.Context, name string, version string, chaincodeType pb.ChaincodeSpec_Type, path string, args [][]byte, creator []byte, channel string, nextBlockNumber uint64, chaincodeSupport *ChaincodeSupport) (*pb.Response, *ccprovider.CCContext, error) {
chaincodeSpec := &pb.ChaincodeSpec{
ChaincodeId: &pb.ChaincodeID{
Name: name,
Expand Down
23 changes: 23 additions & 0 deletions core/chaincode/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,9 @@ func (h *Handler) HandleGetState(msg *pb.ChaincodeMessage, txContext *Transactio
chaincodeLogger.Debugf("[%s] getting state for chaincode %s, key %s, channel %s", shorttxid(msg.Txid), chaincodeName, getState.Key, txContext.ChainID)

if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
if err := errorIfCreatorHasNoReadAccess(chaincodeName, collection, txContext); err != nil {
return nil, err
}
Expand Down Expand Up @@ -663,6 +666,9 @@ func (h *Handler) HandleGetStateMetadata(msg *pb.ChaincodeMessage, txContext *Tr

var metadata map[string][]byte
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
if err := errorIfCreatorHasNoReadAccess(chaincodeName, collection, txContext); err != nil {
return nil, err
}
Expand Down Expand Up @@ -712,6 +718,9 @@ func (h *Handler) HandleGetStateByRange(msg *pb.ChaincodeMessage, txContext *Tra
chaincodeName := h.ChaincodeName()
collection := getStateByRange.Collection
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
if err := errorIfCreatorHasNoReadAccess(chaincodeName, collection, txContext); err != nil {
return nil, err
}
Expand Down Expand Up @@ -833,6 +842,9 @@ func (h *Handler) HandleGetQueryResult(msg *pb.ChaincodeMessage, txContext *Tran
chaincodeName := h.ChaincodeName()
collection := getQueryResult.Collection
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
if err := errorIfCreatorHasNoReadAccess(chaincodeName, collection, txContext); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1006,6 +1018,9 @@ func (h *Handler) HandlePutState(msg *pb.ChaincodeMessage, txContext *Transactio
chaincodeName := h.ChaincodeName()
collection := putState.Collection
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
err = txContext.TXSimulator.SetPrivateData(chaincodeName, collection, putState.Key, putState.Value)
} else {
err = txContext.TXSimulator.SetState(chaincodeName, putState.Key, putState.Value)
Expand Down Expand Up @@ -1035,6 +1050,9 @@ func (h *Handler) HandlePutStateMetadata(msg *pb.ChaincodeMessage, txContext *Tr
chaincodeName := h.ChaincodeName()
collection := putStateMetadata.Collection
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
err = txContext.TXSimulator.SetPrivateDataMetadata(chaincodeName, collection, putStateMetadata.Key, metadata)
} else {
err = txContext.TXSimulator.SetStateMetadata(chaincodeName, putStateMetadata.Key, metadata)
Expand All @@ -1056,6 +1074,9 @@ func (h *Handler) HandleDelState(msg *pb.ChaincodeMessage, txContext *Transactio
chaincodeName := h.ChaincodeName()
collection := delState.Collection
if isCollectionSet(collection) {
if txContext.IsInitTransaction {
return nil, errors.New("private data APIs are not allowed in chaincode Init()")
}
err = txContext.TXSimulator.DeletePrivateData(chaincodeName, collection, delState.Key)
} else {
err = txContext.TXSimulator.DeleteState(chaincodeName, delState.Key)
Expand Down Expand Up @@ -1181,6 +1202,8 @@ func (h *Handler) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovi
defer chaincodeLogger.Debugf("Exit")

txParams.CollectionStore = h.getCollectionStore(msg.ChannelId)
txParams.IsInitTransaction = (msg.Type == pb.ChaincodeMessage_INIT)

txctx, err := h.TXContexts.Create(txParams)
if err != nil {
return nil, err
Expand Down
70 changes: 68 additions & 2 deletions core/chaincode/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ var _ = Describe("Handler", func() {
Expect(value).To(Equal([]byte("put-state-value")))
})

Context("when SetPrivateData fails", func() {
Context("when SetPrivateData fails due to ledger error", func() {
BeforeEach(func() {
fakeTxSimulator.SetPrivateDataReturns(errors.New("godzilla"))
})
Expand All @@ -605,6 +605,17 @@ var _ = Describe("Handler", func() {
Expect(err).To(MatchError("godzilla"))
})
})

Context("when SetPrivateData fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandlePutState(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})
})
})

Expand Down Expand Up @@ -829,7 +840,7 @@ var _ = Describe("Handler", func() {
Expect(key).To(Equal("del-state-key"))
})

Context("when DeletePrivateData returns an error", func() {
Context("when DeletePrivateData fails due to ledger error", func() {
BeforeEach(func() {
fakeTxSimulator.DeletePrivateDataReturns(errors.New("mango"))
})
Expand All @@ -839,6 +850,17 @@ var _ = Describe("Handler", func() {
Expect(err).To(MatchError("mango"))
})
})

Context("when DeletePrivateData fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandleDelState(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})
})
})

Expand Down Expand Up @@ -939,6 +961,17 @@ var _ = Describe("Handler", func() {
})
})

Context("and GetPrivateData fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandleGetState(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})

Context("and GetPrivateData returns the response message", func() {
BeforeEach(func() {
txContext.AllowedCollectionAccess["collection-name"] = true
Expand Down Expand Up @@ -1156,6 +1189,17 @@ var _ = Describe("Handler", func() {
Expect(err).To(MatchError("no collection config"))
})
})

Context("and GetPrivateDataMetadata fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandleGetStateMetadata(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})
})

Context("when collection is not set", func() {
Expand Down Expand Up @@ -1354,6 +1398,17 @@ var _ = Describe("Handler", func() {
Expect(err).To(MatchError("no collection config"))
})
})

Context("and GetPrivateDataRangeScanIterator fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandleGetStateByRange(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})
})

Context("when unmarshalling the request fails", func() {
Expand Down Expand Up @@ -1724,6 +1779,17 @@ var _ = Describe("Handler", func() {
Expect(err).To(MatchError("no collection config"))
})
})

Context("and ExecuteQueryOnPrivateData fails due to Init transaction", func() {
BeforeEach(func() {
txContext.IsInitTransaction = true
})

It("returns the error from errorIfInitTransaction", func() {
_, err := handler.HandleGetQueryResult(incomingMessage, txContext)
Expect(err).To(MatchError("private data APIs are not allowed in chaincode Init()"))
})
})
})

It("builds the query response", func() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package main

import (
"fmt"

"github.com/hyperledger/fabric/core/chaincode/shim"
pb "github.com/hyperledger/fabric/protos/peer"
)

// SimpleChaincode example simple Chaincode implementation
type SimpleChaincode struct {
}

// Init initializes a private state
func (t *SimpleChaincode) Init(stub shim.ChaincodeStubInterface) pb.Response {
if err := stub.PutPrivateData("dummyColl", "dummyKey", []byte("dummyValue")); err != nil {
return shim.Error(fmt.Sprintf("put operation failed. Error storing state: %s", err))
}
return shim.Success(nil)
}

// Invoke is a no-op
func (t *SimpleChaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return shim.Success(nil)
}

func main() {
err := shim.Start(new(SimpleChaincode))
if err != nil {
fmt.Printf("Error starting chaincode: %s", err)
}
}
Loading

0 comments on commit 3e7b2ef

Please sign in to comment.