go_study/fabric-main/gossip/privdata/distributor_test.go

242 lines
7.2 KiB
Go

/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package privdata
import (
"errors"
"fmt"
"testing"
proto "github.com/hyperledger/fabric-protos-go/gossip"
"github.com/hyperledger/fabric-protos-go/peer"
"github.com/hyperledger/fabric-protos-go/transientstore"
"github.com/hyperledger/fabric/core/common/privdata"
"github.com/hyperledger/fabric/gossip/api"
gcommon "github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/gossip/discovery"
"github.com/hyperledger/fabric/gossip/filter"
gossip2 "github.com/hyperledger/fabric/gossip/gossip"
"github.com/hyperledger/fabric/gossip/metrics"
"github.com/hyperledger/fabric/gossip/metrics/mocks"
mocks2 "github.com/hyperledger/fabric/gossip/privdata/mocks"
"github.com/hyperledger/fabric/gossip/protoext"
"github.com/hyperledger/fabric/protoutil"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func Setup(mock *mocks2.CollectionAccessPolicy, requiredPeerCount int, maxPeerCount int,
accessFilter privdata.Filter, orgs map[string]struct{}, memberOnlyRead bool) {
mock.On("AccessFilter").Return(accessFilter)
mock.On("RequiredPeerCount").Return(requiredPeerCount)
mock.On("MaximumPeerCount").Return(maxPeerCount)
mock.On("MemberOrgs").Return(orgs)
mock.On("IsMemberOnlyRead").Return(memberOnlyRead)
}
type gossipMock struct {
err error
mock.Mock
api.PeerSignature
}
func (g *gossipMock) IdentityInfo() api.PeerIdentitySet {
return g.Called().Get(0).(api.PeerIdentitySet)
}
func (g *gossipMock) PeersOfChannel(channelID gcommon.ChannelID) []discovery.NetworkMember {
return g.Called(channelID).Get(0).([]discovery.NetworkMember)
}
func (g *gossipMock) SendByCriteria(message *protoext.SignedGossipMessage, criteria gossip2.SendCriteria) error {
args := g.Called(message, criteria)
if args.Get(0) != nil {
return args.Get(0).(error)
}
return nil
}
func (g *gossipMock) PeerFilter(channel gcommon.ChannelID, messagePredicate api.SubChannelSelectionCriteria) (filter.RoutingFilter, error) {
if g.err != nil {
return nil, g.err
}
return func(member discovery.NetworkMember) bool {
return messagePredicate(g.PeerSignature)
}, nil
}
func TestDistributor(t *testing.T) {
channelID := "test"
g := &gossipMock{
Mock: mock.Mock{},
PeerSignature: api.PeerSignature{
Signature: []byte{3, 4, 5},
Message: []byte{6, 7, 8},
PeerIdentity: []byte{0, 1, 2},
},
}
sendings := make(chan struct {
*proto.PrivatePayload
gossip2.SendCriteria
}, 8)
g.On("PeersOfChannel", gcommon.ChannelID(channelID)).Return([]discovery.NetworkMember{
{PKIid: gcommon.PKIidType{1}},
{PKIid: gcommon.PKIidType{2}},
})
g.On("IdentityInfo").Return(api.PeerIdentitySet{
{
PKIId: gcommon.PKIidType{1},
Organization: api.OrgIdentityType("org1"),
},
{
PKIId: gcommon.PKIidType{2},
Organization: api.OrgIdentityType("org2"),
},
})
g.On("SendByCriteria", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
msg := args.Get(0).(*protoext.SignedGossipMessage)
sendCriteria := args.Get(1).(gossip2.SendCriteria)
sendings <- struct {
*proto.PrivatePayload
gossip2.SendCriteria
}{
PrivatePayload: msg.GetPrivateData().Payload,
SendCriteria: sendCriteria,
}
}).Return(nil)
accessFactoryMock := &mocks2.CollectionAccessFactory{}
c1ColConfig := &peer.CollectionConfig{
Payload: &peer.CollectionConfig_StaticCollectionConfig{
StaticCollectionConfig: &peer.StaticCollectionConfig{
Name: "c1",
RequiredPeerCount: 1,
MaximumPeerCount: 1,
},
},
}
c2ColConfig := &peer.CollectionConfig{
Payload: &peer.CollectionConfig_StaticCollectionConfig{
StaticCollectionConfig: &peer.StaticCollectionConfig{
Name: "c2",
RequiredPeerCount: 1,
MaximumPeerCount: 1,
},
},
}
policyMock := &mocks2.CollectionAccessPolicy{}
Setup(policyMock, 1, 2, func(_ protoutil.SignedData) bool {
return true
}, map[string]struct{}{
"org1": {},
"org2": {},
}, false)
accessFactoryMock.On("AccessPolicy", c1ColConfig, channelID).Return(policyMock, nil)
accessFactoryMock.On("AccessPolicy", c2ColConfig, channelID).Return(policyMock, nil)
testMetricProvider := mocks.TestUtilConstructMetricProvider()
metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics
d := NewDistributor(channelID, g, accessFactoryMock, metrics, 0)
pdFactory := &pvtDataFactory{}
pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1", "c2").create()
err := d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
PvtRwset: pvtData[0].WriteSet,
CollectionConfigs: map[string]*peer.CollectionConfigPackage{
"ns1": {
Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
},
},
}, 0)
require.NoError(t, err)
err = d.Distribute("tx2", &transientstore.TxPvtReadWriteSetWithConfigInfo{
PvtRwset: pvtData[1].WriteSet,
CollectionConfigs: map[string]*peer.CollectionConfigPackage{
"ns2": {
Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
},
},
}, 0)
require.NoError(t, err)
expectedMaxCount := map[string]int{}
expectedMinAck := map[string]int{}
i := 0
require.Len(t, sendings, 8)
for dis := range sendings {
key := fmt.Sprintf("%s~%s", dis.PrivatePayload.Namespace, dis.PrivatePayload.CollectionName)
expectedMaxCount[key] += dis.SendCriteria.MaxPeers
expectedMinAck[key] += dis.SendCriteria.MinAck
i++
if i == 8 {
break
}
}
// Ensure MaxPeers is maxInternalPeers which is 2
require.Equal(t, 2, expectedMaxCount["ns1~c1"])
require.Equal(t, 2, expectedMaxCount["ns2~c2"])
// and MinAck is minInternalPeers which is 1
require.Equal(t, 1, expectedMinAck["ns1~c1"])
require.Equal(t, 1, expectedMinAck["ns2~c2"])
// Channel is empty after we read 8 times from it
require.Len(t, sendings, 0)
// Bad path: dependencies (gossip and others) don't work properly
g.err = errors.New("failed obtaining filter")
err = d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
PvtRwset: pvtData[0].WriteSet,
CollectionConfigs: map[string]*peer.CollectionConfigPackage{
"ns1": {
Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
},
},
}, 0)
require.Error(t, err)
require.Contains(t, err.Error(), "failed obtaining filter")
g.Mock = mock.Mock{}
g.On("SendByCriteria", mock.Anything, mock.Anything).Return(errors.New("failed sending"))
g.On("PeersOfChannel", gcommon.ChannelID(channelID)).Return([]discovery.NetworkMember{
{PKIid: gcommon.PKIidType{1}},
})
g.On("IdentityInfo").Return(api.PeerIdentitySet{
{
PKIId: gcommon.PKIidType{1},
Organization: api.OrgIdentityType("org1"),
},
})
g.err = nil
err = d.Distribute("tx1", &transientstore.TxPvtReadWriteSetWithConfigInfo{
PvtRwset: pvtData[0].WriteSet,
CollectionConfigs: map[string]*peer.CollectionConfigPackage{
"ns1": {
Config: []*peer.CollectionConfig{c1ColConfig, c2ColConfig},
},
},
}, 0)
require.Error(t, err)
require.Contains(t, err.Error(), "Failed disseminating 2 out of 2 private dissemination plans")
require.Equal(t,
[]string{"channel", channelID},
testMetricProvider.FakeSendDuration.WithArgsForCall(0),
)
require.True(t, testMetricProvider.FakeSendDuration.ObserveArgsForCall(0) > 0)
}