Skip to content

Commit

Permalink
[FAB-8941]- extract matching logic from queryconfig
Browse files Browse the repository at this point in the history
Extract Matching logic out of query configblock

Change-Id: I6e5a748bbc63751053c6a41c9d8d999d0e40f7bd
Signed-off-by: Pavan Kappara <pavan.kappara@securekey.com>
  • Loading branch information
Pavan Kappara committed Mar 16, 2018
1 parent ec23384 commit 6763ec7
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 50 deletions.
55 changes: 15 additions & 40 deletions pkg/fab/channel/ledger.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,15 @@ func (c *Ledger) QueryBlockByHash(reqCtx reqContext.Context, blockHash []byte, t
cir := createBlockByHashInvokeRequest(c.chName, blockHash)
tprs, errs := queryChaincode(reqCtx, c.chName, cir, targets, verifier)

responses, errors := getConfigBlocks(tprs)
errs = multi.Append(errs, errors)

return responses, errs
}

func getConfigBlocks(tprs []*fab.TransactionProposalResponse) ([]*common.Block, error) {
responses := []*common.Block{}
var errs error
for _, tpr := range tprs {
r, err := createCommonBlock(tpr)
if err != nil {
Expand All @@ -110,15 +118,8 @@ func (c *Ledger) QueryBlock(reqCtx reqContext.Context, blockNumber uint64, targe
cir := createBlockByNumberInvokeRequest(c.chName, blockNumber)
tprs, errs := queryChaincode(reqCtx, c.chName, cir, targets, verifier)

responses := []*common.Block{}
for _, tpr := range tprs {
r, err := createCommonBlock(tpr)
if err != nil {
errs = multi.Append(errs, errors.WithMessage(err, "From target: "+tpr.Endorser))
} else {
responses = append(responses, r)
}
}
responses, errors := getConfigBlocks(tprs)
errs = multi.Append(errs, errors)
return responses, errs
}

Expand Down Expand Up @@ -190,50 +191,24 @@ func createChaincodeQueryResponse(tpr *fab.TransactionProposalResponse) (*pb.Cha

// QueryConfigBlock returns the current configuration block for the specified channel. If the
// peer doesn't belong to the channel, return error
func (c *Ledger) QueryConfigBlock(reqCtx reqContext.Context, targets []fab.ProposalProcessor, minResponses int, verifier ResponseVerifier) (*common.ConfigEnvelope, error) {
func (c *Ledger) QueryConfigBlock(reqCtx reqContext.Context, targets []fab.ProposalProcessor, verifier ResponseVerifier) (*common.ConfigEnvelope, error) {

if len(targets) == 0 {
return nil, errors.New("target(s) required")
}

if minResponses <= 0 {
return nil, errors.New("Minimum endorser has to be greater than zero")
}

cir := createConfigBlockInvokeRequest(c.chName)
tprs, err := queryChaincode(reqCtx, c.chName, cir, targets, verifier)
if err != nil && len(tprs) == 0 {
return nil, errors.WithMessage(err, "queryChaincode failed")
}

if len(tprs) < minResponses {
return nil, errors.Errorf("Required minimum %d endorsments got %d", minResponses, len(tprs))
}

block, err := createCommonBlock(tprs[0])
if err != nil {
return nil, err
matchErr := verifier.Match(tprs)
if matchErr != nil {
return nil, matchErr
}

// Compare block data from remaining responses
for _, tpr := range tprs[1:] {
b, err := createCommonBlock(tpr)
if err != nil {
return nil, err
}

if !proto.Equal(block.Data, b.Data) {
return nil, errors.New("Payloads for config block do not match")
}
}

if block.Data == nil || block.Data.Data == nil {
return nil, errors.New("config block data is nil")
}

if len(block.Data.Data) != 1 {
return nil, errors.New("config block must contain one transaction")
}
block, _ := createCommonBlock(tprs[0])

return createConfigEnvelope(block.Data.Data[0])

Expand Down
16 changes: 8 additions & 8 deletions pkg/fab/channel/ledger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,19 +125,19 @@ func TestQueryConfig(t *testing.T) {
defer cancel()

// empty targets
_, err := channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{}, 1, nil)
_, err := channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{}, nil)
if err == nil {
t.Fatalf("Should have failed due to empty targets")
}

// min endorsers <= 0
_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{mocks.NewMockPeer("Peer1", "http://peer1.com")}, 0, nil)
_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{mocks.NewMockPeer("Peer1", "http://peer1.com")}, &TransactionProposalResponseVerifier{})
if err == nil {
t.Fatalf("Should have failed due to empty targets")
}

// peer without payload
_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{mocks.NewMockPeer("Peer1", "http://peer1.com")}, 1, nil)
_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{mocks.NewMockPeer("Peer1", "http://peer1.com")}, &TransactionProposalResponseVerifier{MinResponses: 1})
if err == nil {
t.Fatalf("Should have failed due to nil block metadata")
}
Expand Down Expand Up @@ -166,13 +166,13 @@ func TestQueryConfig(t *testing.T) {
peer := mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", MockRoles: []string{}, MockCert: nil, Payload: payload, Status: 200}

// fail with min endorsers
res, err := channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer}, 2, nil)
res, err := channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer}, &TransactionProposalResponseVerifier{MinResponses: 2})
if err == nil {
t.Fatalf("Should have failed with since there's one endorser and at least two are required")
}

// success with one endorser
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer}, 1, nil)
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer}, &TransactionProposalResponseVerifier{MinResponses: 1})
if err != nil || res == nil {
t.Fatalf("Test QueryConfig failed: %v", err)
}
Expand All @@ -181,7 +181,7 @@ func TestQueryConfig(t *testing.T) {
peer2 := mocks.MockPeer{MockName: "Peer2", MockURL: "http://peer2.com", MockRoles: []string{}, MockCert: nil, Payload: payload, Status: 200}

// success with two endorsers
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer, &peer2}, 2, nil)
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer, &peer2}, &TransactionProposalResponseVerifier{MinResponses: 2})
if err != nil || res == nil {
t.Fatalf("Test QueryConfig failed: %v", err)
}
Expand All @@ -207,7 +207,7 @@ func TestQueryConfig(t *testing.T) {

// peer 2 now had different payload; query config block should fail
peer2.Payload = payload2
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer, &peer2}, 2, nil)
res, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer, &peer2}, &TransactionProposalResponseVerifier{MinResponses: 2})
if err == nil {
t.Fatalf("Should have failed for different block payloads")
}
Expand Down Expand Up @@ -246,7 +246,7 @@ func TestQueryConfigBlockDifferentMetadata(t *testing.T) {
reqCtx, cancel := context.NewRequest(setupContext(), context.WithTimeout(10*time.Second))
defer cancel()

_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer1, &peer2}, 2, nil)
_, err = channel.QueryConfigBlock(reqCtx, []fab.ProposalProcessor{&peer1, &peer2}, &TransactionProposalResponseVerifier{MinResponses: 2})
assert.Nil(t, err, "Expected success querying blocks with identical block data payloads")
}

Expand Down
61 changes: 61 additions & 0 deletions pkg/fab/channel/responsevalidator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package channel

import (
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
"github.com/pkg/errors"
)

// TransactionProposalResponseVerifier struct is for verifying TransactionProposalResponse and matches config blocks
type TransactionProposalResponseVerifier struct {
MinResponses int
}

// Verify checks transaction proposal response (empty)
func (tprv *TransactionProposalResponseVerifier) Verify(response *fab.TransactionProposalResponse) error {
return nil
}

// Match verifies and matches transaction proposal responses
func (tprv *TransactionProposalResponseVerifier) Match(transactionProposalResponses []*fab.TransactionProposalResponse) error {
if tprv.MinResponses <= 0 {
return errors.New("minimum Responses has to be greater than zero")
}

if len(transactionProposalResponses) < tprv.MinResponses {
return errors.Errorf("required minimum %d endorsments got %d", tprv.MinResponses, len(transactionProposalResponses))
}

block, err := createCommonBlock(transactionProposalResponses[0])
if err != nil {
return err
}

if block.Data == nil || block.Data.Data == nil {
return errors.New("config block data is nil")
}

if len(block.Data.Data) != 1 {
return errors.New("config block must contain one transaction")
}

// Compare block data from remaining responses
for _, tpr := range transactionProposalResponses[1:] {
b, err := createCommonBlock(tpr)
if err != nil {
return err
}

if !proto.Equal(block.Data, b.Data) {
return errors.New("payloads for config block do not match")
}
}

return nil
}
3 changes: 1 addition & 2 deletions pkg/fab/chconfig/chconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ func (c *ChannelConfig) queryPeers(reqCtx reqContext.Context) (*ChannelCfg, erro
targets = peersToTxnProcessors(c.opts.Targets)
}

// TODO: Verifier implementation Will be handled by different ticket
configEnvelope, err := l.QueryConfigBlock(reqCtx, targets, c.opts.MinResponses, nil)
configEnvelope, err := l.QueryConfigBlock(reqCtx, targets, &channel.TransactionProposalResponseVerifier{MinResponses: c.opts.MinResponses})
if err != nil {
return nil, errors.WithMessage(err, "QueryBlockConfig failed")
}
Expand Down

0 comments on commit 6763ec7

Please sign in to comment.