Skip to content

Commit

Permalink
[FABG-997] Lifecycle query committed chaincode (#116)
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 24, 2020
1 parent 58655d5 commit 7b97fdf
Show file tree
Hide file tree
Showing 6 changed files with 711 additions and 13 deletions.
35 changes: 35 additions & 0 deletions pkg/client/resmgmt/lifecycleclient.go
Expand Up @@ -114,6 +114,28 @@ type LifecycleCommitCCRequest struct {
InitRequired bool
}

// LifecycleQueryCommittedCCRequest contains the parameters to query committed chaincodes.
// If name is not provided then all committed chaincodes on the given channel are returned,
// otherwise only the chaincode with the given name is returned.
type LifecycleQueryCommittedCCRequest struct {
Name string
}

// LifecycleChaincodeDefinition contains information about a committed chaincode.
// Note that approvals are only returned if a chaincode name is provided in the request.
type LifecycleChaincodeDefinition struct {
Name string
Version string
Sequence int64
EndorsementPlugin string
ValidationPlugin string
SignaturePolicy *common.SignaturePolicyEnvelope
ChannelConfigPolicy string
CollectionConfig []*pb.CollectionConfig
InitRequired bool
Approvals map[string]bool
}

// 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 @@ -305,3 +327,16 @@ func (rc *Client) LifecycleCommitCC(channelID string, req LifecycleCommitCCReque

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

// LifecycleQueryCommittedCC queries for committed chaincodes on a given channel
func (rc *Client) LifecycleQueryCommittedCC(channelID string, req LifecycleQueryCommittedCCRequest, options ...RequestOption) ([]LifecycleChaincodeDefinition, error) {
opts, err := rc.prepareRequestOpts(options...)
if err != nil {
return nil, errors.WithMessage(err, "failed to get opts for QueryCommittedCC")
}

reqCtx, cancel := rc.createRequestContext(opts, fab.ResMgmt)
defer cancel()

return rc.lifecycleProcessor.queryCommitted(reqCtx, channelID, req, opts)
}
313 changes: 313 additions & 0 deletions pkg/client/resmgmt/lifecycleclient_test.go
Expand Up @@ -800,3 +800,316 @@ func TestClient_LifecycleCommitCC(t *testing.T) {
})
})
}

func TestClient_LifecycleQueryCommittedCC(t *testing.T) {
const cc1 = "cc1"
const cc2 = "cc2"
const v1 = "v1"
const channel1 = "channel1"

lc := resource.NewLifecycle()
policyBytes, err := lc.MarshalApplicationPolicy(nil, "channel config policy")
require.NoError(t, err)

collections := &pb.CollectionConfigPackage{
Config: []*pb.CollectionConfig{
{
Payload: &pb.CollectionConfig_StaticCollectionConfig{
StaticCollectionConfig: &pb.StaticCollectionConfig{
Name: "coll1",
},
},
},
},
}

lcDef := &lb.QueryChaincodeDefinitionResult{
Sequence: 1,
Version: v1,
ValidationParameter: policyBytes,
Collections: collections,
Approvals: map[string]bool{"org1": true, "org2": false},
}

lcDefBytes, err := proto.Marshal(lcDef)
require.NoError(t, err)

lcDefs := &lb.QueryChaincodeDefinitionsResult{
ChaincodeDefinitions: []*lb.QueryChaincodeDefinitionsResult_ChaincodeDefinition{
{Name: cc1, Sequence: 1, Version: v1, ValidationParameter: policyBytes, Collections: collections},
{Name: cc2, Sequence: 2, Version: v1, ValidationParameter: policyBytes},
},
}

lcDefsBytes, err := proto.Marshal(lcDefs)
require.NoError(t, err)

ctx := setupTestContext("test", "Org1MSP")
ctx.SetEndpointConfig(getNetworkConfig(t))

cs := &MockChannelService{}
transactor := &MockTransactor{}

singleCCResponse := []*fab.TransactionProposalResponse{
{
ProposalResponse: &pb.ProposalResponse{
Response: &pb.Response{
Payload: lcDefBytes,
},
},
},
}

allCCsResponse := []*fab.TransactionProposalResponse{
{
ProposalResponse: &pb.ProposalResponse{
Response: &pb.Response{
Payload: lcDefsBytes,
},
},
},
}

cs.TransactorReturns(transactor, nil)

cp := &MockChannelProvider{}
cp.ChannelServiceReturns(cs, nil)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return nil }
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }

t.Run("With name -> success", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(singleCCResponse, nil)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{Name: cc1}, WithTargets(&fcmocks.MockPeer{}))
require.NoError(t, err)
require.Len(t, resp, 1)
require.Equal(t, cc1, resp[0].Name)
require.Len(t, resp[0].Approvals, 2)
})

t.Run("No name -> success", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(allCCsResponse, nil)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.NoError(t, err)
require.Len(t, resp, 2)
require.Equal(t, cc1, resp[0].Name)
require.Equal(t, cc2, resp[1].Name)
require.Empty(t, resp[0].Approvals)
})

t.Run("With name unmarshal response -> error", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(singleCCResponse, nil)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return nil }
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }
rc.lifecycleProcessor.protoUnmarshal = func(buf []byte, pb proto.Message) error { return fmt.Errorf("injected unmarshal error") }

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{Name: cc1}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, "failed to unmarshal proposal response's response payload: injected unmarshal error")
require.Empty(t, resp)
})

t.Run("With name unmarshal policy error -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected unmarshal error")

ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(singleCCResponse, nil)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return nil }
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }

lr := &MockLifecycleResource{}
lr.UnmarshalApplicationPolicyReturns(nil, "", errExpected)
rc.lifecycleProcessor.lifecycleResource = lr

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{Name: cc1}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, errExpected.Error())
require.Empty(t, resp)
})

t.Run("No name unmarshal response -> error", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(allCCsResponse, nil)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return nil }
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }
rc.lifecycleProcessor.protoUnmarshal = func(buf []byte, pb proto.Message) error { return fmt.Errorf("injected unmarshal error") }

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, "failed to unmarshal proposal response's response payload: injected unmarshal error")
require.Empty(t, resp)
})

t.Run("No name unmarshal policy error -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected unmarshal error")

ctx.SetCustomChannelProvider(cp)
transactor.SendTransactionProposalReturns(allCCsResponse, nil)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return nil }
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }

lr := &MockLifecycleResource{}
lr.UnmarshalApplicationPolicyReturns(nil, "", errExpected)
rc.lifecycleProcessor.lifecycleResource = lr

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, errExpected.Error())
require.Empty(t, resp)
})

t.Run("No channel ID -> error", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)

resp, err := rc.LifecycleQueryCommittedCC("", LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, "channel ID is required")
require.Empty(t, resp)
})

t.Run("Get targets -> error", func(t *testing.T) {
ctx.SetCustomChannelProvider(cp)

errExpected := fmt.Errorf("injected targets error")

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.getCCProposalTargets = func(channelID string, opts requestOptions) ([]fab.Peer, error) { return nil, errExpected }

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, errExpected.Error())
require.Empty(t, resp)
})

t.Run("ChannelService -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected provider error")

cp := &MockChannelProvider{}
cp.ChannelServiceReturns(nil, errExpected)
ctx.SetCustomChannelProvider(cp)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.Error(t, err)
require.Contains(t, err.Error(), errExpected.Error())
require.Empty(t, resp)
})

t.Run("Transactor -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected transactor error")
cs := &MockChannelService{}
cs.TransactorReturns(nil, errExpected)

cp := &MockChannelProvider{}
cp.ChannelServiceReturns(cs, nil)

ctx.SetCustomChannelProvider(cp)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.Error(t, err)
require.Contains(t, err.Error(), errExpected.Error())
require.Empty(t, resp)
})

t.Run("Signature -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected signature error")

ctx.SetCustomChannelProvider(cp)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }
rc.lifecycleProcessor.verifyTPSignature = func(fab.ChannelService, []*fab.TransactionProposalResponse) error { return errExpected }

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.Error(t, err)
require.Contains(t, err.Error(), errExpected.Error())
require.Empty(t, resp)
})

t.Run("CreateProposal -> error", func(t *testing.T) {
errExpected := fmt.Errorf("injected create proposal error")

ctx.SetCustomChannelProvider(cp)

rc := setupResMgmtClient(t, ctx, getDefaultTargetFilterOption())
rc.lifecycleProcessor.getCCProposalTargets = func(string, requestOptions) ([]fab.Peer, error) { return []fab.Peer{&fcmocks.MockPeer{}}, nil }

lr := &MockLifecycleResource{}
lr.CreateQueryCommittedProposalReturns(nil, errExpected)

rc.lifecycleProcessor.lifecycleResource = lr

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.Error(t, err)
require.Contains(t, err.Error(), errExpected.Error())
require.Empty(t, resp)
})

t.Run("No responses -> error", func(t *testing.T) {
cs := &MockChannelService{}
transactor := &MockTransactor{}

transactor.SendTransactionProposalReturns(nil, nil)
cs.TransactorReturns(transactor, nil)

cp := &MockChannelProvider{}
cp.ChannelServiceReturns(cs, nil)

ctx.SetCustomChannelProvider(cp)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, "no responses")
require.Empty(t, resp)
})

t.Run("Endorsements not matching -> error", func(t *testing.T) {
responseBytes1, err := proto.Marshal(&lb.CheckCommitReadinessResult{
Approvals: map[string]bool{"org1": true, "org2": false},
})
require.NoError(t, err)

responseBytes2, err := proto.Marshal(&lb.CheckCommitReadinessResult{
Approvals: map[string]bool{"org3": true},
})
require.NoError(t, err)

cs := &MockChannelService{}
transactor := &MockTransactor{}

result := []*fab.TransactionProposalResponse{
{
ProposalResponse: &pb.ProposalResponse{
Response: &pb.Response{
Payload: responseBytes1,
},
},
},
{
ProposalResponse: &pb.ProposalResponse{
Response: &pb.Response{
Payload: responseBytes2,
},
},
},
}

transactor.SendTransactionProposalReturns(result, nil)
cs.TransactorReturns(transactor, nil)

cp := &MockChannelProvider{}
cp.ChannelServiceReturns(cs, nil)

ctx.SetCustomChannelProvider(cp)

resp, err := rc.LifecycleQueryCommittedCC(channel1, LifecycleQueryCommittedCCRequest{}, WithTargets(&fcmocks.MockPeer{}))
require.EqualError(t, err, "responses from endorsers do not match")
require.Empty(t, resp)
})
}

0 comments on commit 7b97fdf

Please sign in to comment.