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