Skip to content

Commit

Permalink
[FABG-994] Implement lifecycle query approved chaincode (#113)
Browse files Browse the repository at this point in the history
Signed-off-by: Bob Stasyszyn <Bob.Stasyszyn@securekey.com>
  • Loading branch information
bstasyszyn committed Jul 23, 2020
1 parent ec04055 commit 9e1c2cf
Show file tree
Hide file tree
Showing 7 changed files with 557 additions and 3 deletions.
43 changes: 43 additions & 0 deletions pkg/client/resmgmt/lifecycleclient.go
Expand Up @@ -62,6 +62,26 @@ type LifecycleApproveCCRequest struct {
InitRequired bool
}

// LifecycleQueryApprovedCCRequest contains the parameters for querying approved chaincodes
type LifecycleQueryApprovedCCRequest struct {
Name string
Sequence int64
}

// LifecycleApprovedChaincodeDefinition contains information about the approved chaincode
type LifecycleApprovedChaincodeDefinition struct {
Name string
Version string
Sequence int64
EndorsementPlugin string
ValidationPlugin string
SignaturePolicy *common.SignaturePolicyEnvelope
ChannelConfigPolicy string
CollectionConfig []*pb.CollectionConfig
InitRequired bool
PackageID string
}

// LifecycleInstallCC installs a chaincode package using Fabric 2.0 chaincode lifecycle.
func (rc *Client) LifecycleInstallCC(req LifecycleInstallCCRequest, options ...RequestOption) ([]LifecycleInstallCCResponse, error) {
err := rc.lifecycleProcessor.verifyInstallParams(req)
Expand Down Expand Up @@ -204,3 +224,26 @@ func (rc *Client) LifecycleApproveCC(channelID string, req LifecycleApproveCCReq

return rc.lifecycleProcessor.approve(reqCtx, channelID, req, opts)
}

// LifecycleQueryApprovedCC returns information about the approved chaincode definition
func (rc *Client) LifecycleQueryApprovedCC(channelID string, req LifecycleQueryApprovedCCRequest, options ...RequestOption) (LifecycleApprovedChaincodeDefinition, error) {
opts, err := rc.prepareRequestOpts(options...)
if err != nil {
return LifecycleApprovedChaincodeDefinition{}, errors.WithMessage(err, "failed to get opts for QueryApprovedCCDefinition")
}

if len(opts.Targets) != 1 {
return LifecycleApprovedChaincodeDefinition{}, errors.New("only one target is supported")
}

rc.resolveTimeouts(&opts)

parentReqCtx, parentReqCancel := contextImpl.NewRequest(rc.ctx, contextImpl.WithTimeout(opts.Timeouts[fab.ResMgmt]), contextImpl.WithParent(opts.ParentContext))
parentReqCtx = reqContext.WithValue(parentReqCtx, contextImpl.ReqContextTimeoutOverrides, opts.Timeouts)
defer parentReqCancel()

reqCtx, cancel := contextImpl.NewRequest(rc.ctx, contextImpl.WithTimeoutType(fab.ResMgmt), contextImpl.WithParent(parentReqCtx))
defer cancel()

return rc.lifecycleProcessor.queryApproved(reqCtx, channelID, req, opts.Targets[0])
}
83 changes: 83 additions & 0 deletions pkg/client/resmgmt/lifecycleclient_test.go
Expand Up @@ -12,13 +12,15 @@ import (
"testing"

"github.com/golang/protobuf/proto"
pb "github.com/hyperledger/fabric-protos-go/peer"
lb "github.com/hyperledger/fabric-protos-go/peer/lifecycle"
"github.com/stretchr/testify/require"

"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
lifecyclepkg "github.com/hyperledger/fabric-sdk-go/pkg/fab/ccpackager/lifecycle"
fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/resource"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/policydsl"
)

func TestClient_LifecycleInstallCC(t *testing.T) {
Expand Down Expand Up @@ -352,3 +354,84 @@ func TestClient_LifecycleApproveCC(t *testing.T) {
})
})
}

func TestClient_LifecycleQueryApprovedCC(t *testing.T) {
const packageID = "pkg1"
const cc1 = "cc1"
const v1 = "v1"
const channel1 = "channel1"

applicationPolicy := &pb.ApplicationPolicy{
Type: &pb.ApplicationPolicy_SignaturePolicy{
SignaturePolicy: policydsl.AcceptAllPolicy,
},
}

policyBytes, err := proto.Marshal(applicationPolicy)
require.NoError(t, err)

response := &lb.QueryApprovedChaincodeDefinitionResult{
Sequence: 1,
Version: v1,
ValidationParameter: policyBytes,
Source: &lb.ChaincodeSource{
Type: &lb.ChaincodeSource_LocalPackage{
LocalPackage: &lb.ChaincodeSource_Local{
PackageId: packageID,
},
},
},
Collections: &pb.CollectionConfigPackage{},
}

responseBytes, err := proto.Marshal(response)
require.NoError(t, err)

peer1 := &fcmocks.MockPeer{Payload: responseBytes}

req := LifecycleQueryApprovedCCRequest{
Name: cc1,
Sequence: 1,
}

t.Run("Success", func(t *testing.T) {
rc := setupDefaultResMgmtClient(t)

resp, err := rc.LifecycleQueryApprovedCC(channel1, req, WithTargets(peer1))
require.NoError(t, err)
require.NotNil(t, resp)
})

t.Run("No targets", func(t *testing.T) {
rc := setupDefaultResMgmtClient(t)

resp, err := rc.LifecycleQueryApprovedCC(channel1, req)
require.EqualError(t, err, "only one target is supported")
require.Empty(t, resp)
})

t.Run("Marshal error", func(t *testing.T) {
rc := setupDefaultResMgmtClient(t)

resp, err := rc.LifecycleQueryApprovedCC(channel1, req, WithTargets(&fcmocks.MockPeer{Payload: []byte("invalid payload")}))
require.Error(t, err)
require.Contains(t, err.Error(), "failed to unmarshal proposal response's response payload")
require.Empty(t, resp)
})

t.Run("No channel ID -> error", func(t *testing.T) {
rc := setupDefaultResMgmtClient(t)

resp, err := rc.LifecycleQueryApprovedCC("", req, WithTargets(peer1))
require.EqualError(t, err, "channel ID is required")
require.Empty(t, resp)
})

t.Run("No name -> error", func(t *testing.T) {
rc := setupDefaultResMgmtClient(t)

resp, err := rc.LifecycleQueryApprovedCC(channel1, LifecycleQueryApprovedCCRequest{}, WithTargets(peer1))
require.EqualError(t, err, "name is required")
require.Empty(t, resp)
})
}
33 changes: 33 additions & 0 deletions pkg/client/resmgmt/lifecycleprocessor.go
Expand Up @@ -32,6 +32,7 @@ type lifecycleResource interface {
Install(reqCtx reqContext.Context, installPkg []byte, targets []fab.ProposalProcessor, opts ...resource.Opt) ([]*resource.LifecycleInstallProposalResponse, error)
GetInstalledPackage(reqCtx reqContext.Context, packageID string, target fab.ProposalProcessor, opts ...resource.Opt) ([]byte, error)
QueryInstalled(reqCtx reqContext.Context, target fab.ProposalProcessor, opts ...resource.Opt) (*resource.LifecycleQueryInstalledCCResponse, error)
QueryApproved(reqCtx reqContext.Context, channelID string, req *resource.QueryApprovedChaincodeRequest, target fab.ProposalProcessor, opts ...resource.Opt) (*resource.LifecycleQueryApprovedCCResponse, error)
CreateApproveProposal(txh fab.TransactionHeader, req *resource.ApproveChaincodeRequest) (*fab.TransactionProposal, error)
}

Expand Down Expand Up @@ -143,6 +144,26 @@ func (p *lifecycleProcessor) approve(reqCtx reqContext.Context, channelID string
return p.commitTransaction(eventService, tp, txProposalResponse, transactor, reqCtx)
}

func (p *lifecycleProcessor) queryApproved(reqCtx reqContext.Context, channelID string, req LifecycleQueryApprovedCCRequest, target fab.Peer) (LifecycleApprovedChaincodeDefinition, error) {
if err := p.verifyQueryApprovedParams(channelID, req); err != nil {
return LifecycleApprovedChaincodeDefinition{}, err
}

r := &resource.QueryApprovedChaincodeRequest{
Name: req.Name,
Sequence: req.Sequence,
}

tpr, err := p.QueryApproved(reqCtx, channelID, r, target)
if err != nil {
return LifecycleApprovedChaincodeDefinition{}, errors.WithMessage(err, "querying for installed chaincode failed")
}

logger.Debugf("Query approved chaincodes endorser '%s' returned ProposalResponse status:%v", tpr.Endorser, tpr.Status)

return LifecycleApprovedChaincodeDefinition(*tpr.ApprovedChaincode), nil
}

func (p *lifecycleProcessor) adjustTargetsForInstall(targets []fab.Peer, req LifecycleInstallCCRequest, retry retry.Opts, parentReqCtx reqContext.Context) ([]fab.Peer, multi.Errors) {
errs := multi.Errors{}

Expand Down Expand Up @@ -196,6 +217,18 @@ func (p *lifecycleProcessor) verifyApproveParams(channelID string, req Lifecycle
return nil
}

func (p *lifecycleProcessor) verifyQueryApprovedParams(channelID string, req LifecycleQueryApprovedCCRequest) error {
if channelID == "" {
return errors.New("channel ID is required")
}

if req.Name == "" {
return errors.New("name is required")
}

return nil
}

func (p *lifecycleProcessor) isInstalled(reqCtx reqContext.Context, req LifecycleInstallCCRequest, peer fab.ProposalProcessor, retryOpts retry.Opts) (bool, error) {
packageID := lifecyclepkg.ComputePackageID(req.Label, req.Package)

Expand Down
74 changes: 74 additions & 0 deletions pkg/client/resmgmt/mocklifecycleresource.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 21 additions & 1 deletion pkg/fab/resource/api.go
Expand Up @@ -81,8 +81,28 @@ type LifecycleInstalledCC struct {
References map[string][]CCReference
}

// LifecycleQueryInstalledCCResponse contains the response for a QueryInstalledCC request.
// LifecycleQueryInstalledCCResponse contains the response for a LifecycleQueryInstalledCC request.
type LifecycleQueryInstalledCCResponse struct {
*fab.TransactionProposalResponse
InstalledChaincodes []LifecycleInstalledCC
}

// LifecycleQueryApprovedCCResponse contains the response for a LifecycleQueryApprovedCC request
type LifecycleQueryApprovedCCResponse struct {
*fab.TransactionProposalResponse
ApprovedChaincode *LifecycleApprovedCC
}

// LifecycleApprovedCC contains information about an approved chaincode
type LifecycleApprovedCC struct {
Name string
Version string
Sequence int64
EndorsementPlugin string
ValidationPlugin string
SignaturePolicy *common.SignaturePolicyEnvelope
ChannelConfigPolicy string
CollectionConfig []*pb.CollectionConfig
InitRequired bool
PackageID string
}

0 comments on commit 9e1c2cf

Please sign in to comment.