github.com/true-sqn/fabric@v2.1.1+incompatible/gossip/privdata/distributor_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privdata
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  	"testing"
    13  
    14  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    15  	"github.com/hyperledger/fabric-protos-go/peer"
    16  	"github.com/hyperledger/fabric-protos-go/transientstore"
    17  	"github.com/hyperledger/fabric/core/common/privdata"
    18  	"github.com/hyperledger/fabric/gossip/api"
    19  	gcommon "github.com/hyperledger/fabric/gossip/common"
    20  	"github.com/hyperledger/fabric/gossip/discovery"
    21  	"github.com/hyperledger/fabric/gossip/filter"
    22  	gossip2 "github.com/hyperledger/fabric/gossip/gossip"
    23  	"github.com/hyperledger/fabric/gossip/metrics"
    24  	"github.com/hyperledger/fabric/gossip/metrics/mocks"
    25  	mocks2 "github.com/hyperledger/fabric/gossip/privdata/mocks"
    26  	"github.com/hyperledger/fabric/gossip/protoext"
    27  	"github.com/hyperledger/fabric/protoutil"
    28  	"github.com/stretchr/testify/assert"
    29  	"github.com/stretchr/testify/mock"
    30  )
    31  
    32  func Setup(mock *mocks2.CollectionAccessPolicy, requiredPeerCount int, maxPeerCount int,
    33  	accessFilter privdata.Filter, orgs map[string]struct{}, memberOnlyRead bool) {
    34  	mock.On("AccessFilter").Return(accessFilter)
    35  	mock.On("RequiredPeerCount").Return(requiredPeerCount)
    36  	mock.On("MaximumPeerCount").Return(maxPeerCount)
    37  	mock.On("MemberOrgs").Return(orgs)
    38  	mock.On("IsMemberOnlyRead").Return(memberOnlyRead)
    39  }
    40  
    41  type gossipMock struct {
    42  	err error
    43  	mock.Mock
    44  	api.PeerSignature
    45  }
    46  
    47  func (g *gossipMock) IdentityInfo() api.PeerIdentitySet {
    48  	return g.Called().Get(0).(api.PeerIdentitySet)
    49  }
    50  
    51  func (g *gossipMock) PeersOfChannel(channelID gcommon.ChannelID) []discovery.NetworkMember {
    52  	return g.Called(channelID).Get(0).([]discovery.NetworkMember)
    53  }
    54  
    55  func (g *gossipMock) SendByCriteria(message *protoext.SignedGossipMessage, criteria gossip2.SendCriteria) error {
    56  	args := g.Called(message, criteria)
    57  	if args.Get(0) != nil {
    58  		return args.Get(0).(error)
    59  	}
    60  	return nil
    61  }
    62  
    63  func (g *gossipMock) PeerFilter(channel gcommon.ChannelID, messagePredicate api.SubChannelSelectionCriteria) (filter.RoutingFilter, error) {
    64  	if g.err != nil {
    65  		return nil, g.err
    66  	}
    67  	return func(member discovery.NetworkMember) bool {
    68  		return messagePredicate(g.PeerSignature)
    69  	}, nil
    70  }
    71  
    72  func TestDistributor(t *testing.T) {
    73  	channelID := "test"
    74  
    75  	g := &gossipMock{
    76  		Mock: mock.Mock{},
    77  		PeerSignature: api.PeerSignature{
    78  			Signature:    []byte{3, 4, 5},
    79  			Message:      []byte{6, 7, 8},
    80  			PeerIdentity: []byte{0, 1, 2},
    81  		},
    82  	}
    83  	sendings := make(chan struct {
    84  		*proto.PrivatePayload
    85  		gossip2.SendCriteria
    86  	}, 8)
    87  
    88  	g.On("PeersOfChannel", gcommon.ChannelID(channelID)).Return([]discovery.NetworkMember{
    89  		{PKIid: gcommon.PKIidType{1}},
    90  		{PKIid: gcommon.PKIidType{2}},
    91  	})
    92  
    93  	g.On("IdentityInfo").Return(api.PeerIdentitySet{
    94  		{
    95  			PKIId:        gcommon.PKIidType{1},
    96  			Organization: api.OrgIdentityType("org1"),
    97  		},
    98  		{
    99  			PKIId:        gcommon.PKIidType{2},
   100  			Organization: api.OrgIdentityType("org2"),
   101  		},
   102  	})
   103  
   104  	g.On("SendByCriteria", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   105  		msg := args.Get(0).(*protoext.SignedGossipMessage)
   106  		sendCriteria := args.Get(1).(gossip2.SendCriteria)
   107  		sendings <- struct {
   108  			*proto.PrivatePayload
   109  			gossip2.SendCriteria
   110  		}{
   111  			PrivatePayload: msg.GetPrivateData().Payload,
   112  			SendCriteria:   sendCriteria,
   113  		}
   114  	}).Return(nil)
   115  	accessFactoryMock := &mocks2.CollectionAccessFactory{}
   116  	c1ColConfig := &peer.CollectionConfig{
   117  		Payload: &peer.CollectionConfig_StaticCollectionConfig{
   118  			StaticCollectionConfig: &peer.StaticCollectionConfig{
   119  				Name:              "c1",
   120  				RequiredPeerCount: 1,
   121  				MaximumPeerCount:  1,
   122  			},
   123  		},
   124  	}
   125  
   126  	c2ColConfig := &peer.CollectionConfig{
   127  		Payload: &peer.CollectionConfig_StaticCollectionConfig{
   128  			StaticCollectionConfig: &peer.StaticCollectionConfig{
   129  				Name:              "c2",
   130  				RequiredPeerCount: 1,
   131  				MaximumPeerCount:  1,
   132  			},
   133  		},
   134  	}
   135  
   136  	policyMock := &mocks2.CollectionAccessPolicy{}
   137  	Setup(policyMock, 1, 2, func(_ protoutil.SignedData) bool {
   138  		return true
   139  	}, map[string]struct{}{
   140  		"org1": {},
   141  		"org2": {},
   142  	}, false)
   143  
   144  	accessFactoryMock.On("AccessPolicy", c1ColConfig, channelID).Return(policyMock, nil)
   145  	accessFactoryMock.On("AccessPolicy", c2ColConfig, channelID).Return(policyMock, nil)
   146  
   147  	testMetricProvider := mocks.TestUtilConstructMetricProvider()
   148  	metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics
   149  
   150  	d := NewDistributor(channelID, g, accessFactoryMock, metrics, 0)
   151  	pdFactory := &pvtDataFactory{}
   152  	pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1", "c2").create()
   153  	err := d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
   154  		PvtRwset: pvtData[0].WriteSet,
   155  		CollectionConfigs: map[string]*peer.CollectionConfigPackage{
   156  			"ns1": {
   157  				Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
   158  			},
   159  		},
   160  	}, 0)
   161  	assert.NoError(t, err)
   162  	err = d.Distribute("tx2", &transientstore.TxPvtReadWriteSetWithConfigInfo{
   163  		PvtRwset: pvtData[1].WriteSet,
   164  		CollectionConfigs: map[string]*peer.CollectionConfigPackage{
   165  			"ns2": {
   166  				Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
   167  			},
   168  		},
   169  	}, 0)
   170  	assert.NoError(t, err)
   171  
   172  	expectedMaxCount := map[string]int{}
   173  	expectedMinAck := map[string]int{}
   174  
   175  	i := 0
   176  	assert.Len(t, sendings, 8)
   177  	for dis := range sendings {
   178  		key := fmt.Sprintf("%s~%s", dis.PrivatePayload.Namespace, dis.PrivatePayload.CollectionName)
   179  		expectedMaxCount[key] += dis.SendCriteria.MaxPeers
   180  		expectedMinAck[key] += dis.SendCriteria.MinAck
   181  		i++
   182  		if i == 8 {
   183  			break
   184  		}
   185  	}
   186  
   187  	// Ensure MaxPeers is maxInternalPeers which is 2
   188  	assert.Equal(t, 2, expectedMaxCount["ns1~c1"])
   189  	assert.Equal(t, 2, expectedMaxCount["ns2~c2"])
   190  
   191  	// and MinAck is minInternalPeers which is 1
   192  	assert.Equal(t, 1, expectedMinAck["ns1~c1"])
   193  	assert.Equal(t, 1, expectedMinAck["ns2~c2"])
   194  
   195  	// Channel is empty after we read 8 times from it
   196  	assert.Len(t, sendings, 0)
   197  
   198  	// Bad path: dependencies (gossip and others) don't work properly
   199  	g.err = errors.New("failed obtaining filter")
   200  	err = d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
   201  		PvtRwset: pvtData[0].WriteSet,
   202  		CollectionConfigs: map[string]*peer.CollectionConfigPackage{
   203  			"ns1": {
   204  				Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
   205  			},
   206  		},
   207  	}, 0)
   208  	assert.Error(t, err)
   209  	assert.Contains(t, err.Error(), "failed obtaining filter")
   210  
   211  	g.Mock = mock.Mock{}
   212  	g.On("SendByCriteria", mock.Anything, mock.Anything).Return(errors.New("failed sending"))
   213  	g.On("PeersOfChannel", gcommon.ChannelID(channelID)).Return([]discovery.NetworkMember{
   214  		{PKIid: gcommon.PKIidType{1}},
   215  	})
   216  
   217  	g.On("IdentityInfo").Return(api.PeerIdentitySet{
   218  		{
   219  			PKIId:        gcommon.PKIidType{1},
   220  			Organization: api.OrgIdentityType("org1"),
   221  		},
   222  	})
   223  
   224  	g.err = nil
   225  	err = d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
   226  		PvtRwset: pvtData[0].WriteSet,
   227  		CollectionConfigs: map[string]*peer.CollectionConfigPackage{
   228  			"ns1": {
   229  				Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
   230  			},
   231  		},
   232  	}, 0)
   233  	assert.Error(t, err)
   234  	assert.Contains(t, err.Error(), "Failed disseminating 2 out of 2 private dissemination plans")
   235  
   236  	assert.Equal(t,
   237  		[]string{"channel", channelID},
   238  		testMetricProvider.FakeSendDuration.WithArgsForCall(0),
   239  	)
   240  	assert.True(t, testMetricProvider.FakeSendDuration.ObserveArgsForCall(0) > 0)
   241  }