Skip to content

Commit

Permalink
[FAB-8900] random max targets for config block
Browse files Browse the repository at this point in the history
Chconfig currently fetches config block from all
configured peers. change has been made to select
max random targets from low level chconfig opts.

TO DO: The number of peers also need to be exposed as option
in higher level clients or in config.

Change-Id: Ib94fb3ec96291e8af0a5bcdb38f3c3d9655407b2
Signed-off-by: Sudesh Shetty <[email protected]>
  • Loading branch information
sudeshrshetty committed Mar 15, 2018
1 parent 523348b commit 461e4d7
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 8 deletions.
43 changes: 36 additions & 7 deletions pkg/fab/chconfig/chconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package chconfig

import (
reqContext "context"
"math/rand"

"github.com/golang/protobuf/proto"

Expand All @@ -33,13 +34,15 @@ var logger = logging.NewLogger("fabsdk/fab")

const (
defaultMinResponses = 1
defaultMaxTargets = 2
)

// Opts contains options for retrieving channel configuration
type Opts struct {
Orderer fab.Orderer // if configured, channel config will be retrieved from this orderer
Targets []fab.Peer // if configured, channel config will be retrieved from peers (targets)
MinResponses int // used with targets option; min number of success responses (from targets/peers)
MaxTargets int //if configured, channel config will be retrieved for these number of random targets
}

// Option func for each Opts argument
Expand Down Expand Up @@ -145,18 +148,15 @@ func (c *ChannelConfig) queryPeers(reqCtx reqContext.Context) (*ChannelCfg, erro
}

targets = append(targets, newPeer)

}

targets = randomMaxTargets(targets, c.opts.MaxTargets)

} else {
targets = peersToTxnProcessors(c.opts.Targets)
}

minEndorsers := c.opts.MinResponses
if minEndorsers == 0 {
minEndorsers = defaultMinResponses
}

configEnvelope, err := l.QueryConfigBlock(reqCtx, targets, minEndorsers)
configEnvelope, err := l.QueryConfigBlock(reqCtx, targets, c.opts.MinResponses)
if err != nil {
return nil, errors.WithMessage(err, "QueryBlockConfig failed")
}
Expand Down Expand Up @@ -198,6 +198,14 @@ func WithOrderer(orderer fab.Orderer) Option {
}
}

// WithMaxTargets encapsulates minTargets to Option
func WithMaxTargets(maxTargets int) Option {
return func(opts *Opts) error {
opts.MaxTargets = maxTargets
return nil
}
}

// prepareQueryConfigOpts Reads channel config options from Option array
func prepareOpts(options ...Option) (Opts, error) {
opts := Opts{}
Expand All @@ -207,6 +215,15 @@ func prepareOpts(options ...Option) (Opts, error) {
return opts, errors.WithMessage(err, "Failed to read query config opts")
}
}

//resolve defaults
if opts.MinResponses == 0 {
opts.MinResponses = defaultMinResponses
}
if opts.MaxTargets == 0 {
opts.MaxTargets = defaultMaxTargets
}

return opts, nil
}

Expand Down Expand Up @@ -481,3 +498,15 @@ func peersToTxnProcessors(peers []fab.Peer) []fab.ProposalProcessor {
}
return tpp
}

//randomMaxTargets returns random sub set of max length targets
func randomMaxTargets(targets []fab.ProposalProcessor, max int) []fab.ProposalProcessor {
if len(targets) <= max {
return targets
}
for i := range targets {
j := rand.Intn(i + 1)
targets[i], targets[j] = targets[j], targets[i]
}
return targets[:max]
}
48 changes: 47 additions & 1 deletion pkg/fab/chconfig/chconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ SPDX-License-Identifier: Apache-2.0
package chconfig

import (
reqContext "context"
"testing"

"time"
Expand All @@ -17,6 +18,8 @@ import (
"github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks"
"github.com/hyperledger/fabric-sdk-go/pkg/fab/orderer"
mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/mocks"
"github.com/pkg/errors"

"github.com/stretchr/testify/assert"
)

Expand All @@ -29,7 +32,7 @@ func TestChannelConfigWithPeer(t *testing.T) {
ctx := setupTestContext()
peer := getPeerWithConfigBlockPayload(t)

channelConfig, err := New(channelID, WithPeers([]fab.Peer{peer}), WithMinResponses(1))
channelConfig, err := New(channelID, WithPeers([]fab.Peer{peer}), WithMinResponses(1), WithMaxTargets(1))
if err != nil {
t.Fatalf("Failed to create new channel client: %s", err)
}
Expand Down Expand Up @@ -87,6 +90,40 @@ func TestChannelConfigWithOrdererError(t *testing.T) {

}

func TestRandomMaxTargetsSelections(t *testing.T) {

testTargets := []fab.ProposalProcessor{
&mockProposalProcessor{"ONE"}, &mockProposalProcessor{"TWO"}, &mockProposalProcessor{"THREE"},
&mockProposalProcessor{"FOUR"}, &mockProposalProcessor{"FIVE"}, &mockProposalProcessor{"SIX"},
&mockProposalProcessor{"SEVEN"}, &mockProposalProcessor{"EIGHT"}, &mockProposalProcessor{"NINE"},
}

max := 3
before := ""
for _, v := range testTargets[:max] {
before = before + v.(*mockProposalProcessor).name
}

responseTargets := randomMaxTargets(testTargets, max)
assert.True(t, responseTargets != nil && len(responseTargets) == max, "response target not as expected")

after := ""
for _, v := range responseTargets {
after = after + v.(*mockProposalProcessor).name
}
//make sure it is random
assert.False(t, before == after, "response targets are not random")

max = 0 //when zero minimum supplied, result should be empty
responseTargets = randomMaxTargets(testTargets, max)
assert.True(t, responseTargets != nil && len(responseTargets) == max, "response target not as expected")

max = 12 //greater than targets length
responseTargets = randomMaxTargets(testTargets, max)
assert.True(t, responseTargets != nil && len(responseTargets) == len(testTargets), "response target not as expected")

}

func setupTestContext() context.Client {
user := mspmocks.NewMockSigningIdentity("test", "test")
ctx := mocks.NewMockContext(user)
Expand Down Expand Up @@ -121,6 +158,15 @@ func getPeerWithConfigBlockPayload(t *testing.T) fab.Peer {
return peer
}

//mockProposalProcessor to mock proposal processor for random max target test
type mockProposalProcessor struct {
name string
}

func (pp *mockProposalProcessor) ProcessTransactionProposal(reqCtx reqContext.Context, request fab.ProcessProposalRequest) (*fab.TransactionProposalResponse, error) {
return nil, errors.New("not implemented, just mock")
}

var validRootCA = `-----BEGIN CERTIFICATE-----
MIICYjCCAgmgAwIBAgIUB3CTDOU47sUC5K4kn/Caqnh114YwCgYIKoZIzj0EAwIw
fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh
Expand Down

0 comments on commit 461e4d7

Please sign in to comment.