github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/gossip/privdata/coordinator_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  	"encoding/asn1"
    11  	"encoding/hex"
    12  	"errors"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"os"
    16  	"reflect"
    17  	"testing"
    18  	"time"
    19  
    20  	pb "github.com/golang/protobuf/proto"
    21  	"github.com/hyperledger/fabric-protos-go/common"
    22  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    23  	"github.com/hyperledger/fabric-protos-go/ledger/rwset"
    24  	"github.com/hyperledger/fabric-protos-go/ledger/rwset/kvrwset"
    25  	mspproto "github.com/hyperledger/fabric-protos-go/msp"
    26  	"github.com/hyperledger/fabric-protos-go/peer"
    27  	tspb "github.com/hyperledger/fabric-protos-go/transientstore"
    28  	"github.com/hyperledger/fabric/bccsp/factory"
    29  	"github.com/hyperledger/fabric/common/metrics/disabled"
    30  	util2 "github.com/hyperledger/fabric/common/util"
    31  	"github.com/hyperledger/fabric/core/common/privdata"
    32  	"github.com/hyperledger/fabric/core/ledger"
    33  	"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/rwsetutil"
    34  	"github.com/hyperledger/fabric/core/transientstore"
    35  	"github.com/hyperledger/fabric/gossip/metrics"
    36  	gmetricsmocks "github.com/hyperledger/fabric/gossip/metrics/mocks"
    37  	privdatacommon "github.com/hyperledger/fabric/gossip/privdata/common"
    38  	"github.com/hyperledger/fabric/gossip/privdata/mocks"
    39  	capabilitymock "github.com/hyperledger/fabric/gossip/privdata/mocks"
    40  	"github.com/hyperledger/fabric/gossip/util"
    41  	"github.com/hyperledger/fabric/msp"
    42  	"github.com/hyperledger/fabric/msp/mgmt"
    43  	mspmgmt "github.com/hyperledger/fabric/msp/mgmt"
    44  	msptesttools "github.com/hyperledger/fabric/msp/mgmt/testtools"
    45  	"github.com/hyperledger/fabric/protoutil"
    46  	"github.com/stretchr/testify/assert"
    47  	"github.com/stretchr/testify/mock"
    48  	"github.com/stretchr/testify/require"
    49  )
    50  
    51  var testConfig = CoordinatorConfig{
    52  	PullRetryThreshold:             time.Second * 3,
    53  	TransientBlockRetention:        1000,
    54  	SkipPullingInvalidTransactions: false,
    55  }
    56  
    57  // CollectionCriteria aggregates criteria of
    58  // a collection
    59  type CollectionCriteria struct {
    60  	Channel    string
    61  	Collection string
    62  	Namespace  string
    63  }
    64  
    65  func fromCollectionCriteria(criteria privdata.CollectionCriteria) CollectionCriteria {
    66  	return CollectionCriteria{
    67  		Collection: criteria.Collection,
    68  		Namespace:  criteria.Namespace,
    69  		Channel:    criteria.Channel,
    70  	}
    71  }
    72  
    73  type validatorMock struct {
    74  	err error
    75  }
    76  
    77  func (v *validatorMock) Validate(block *common.Block) error {
    78  	if v.err != nil {
    79  		return v.err
    80  	}
    81  	return nil
    82  }
    83  
    84  type digests []privdatacommon.DigKey
    85  
    86  func (d digests) Equal(other digests) bool {
    87  	flatten := func(d digests) map[privdatacommon.DigKey]struct{} {
    88  		m := map[privdatacommon.DigKey]struct{}{}
    89  		for _, dig := range d {
    90  			m[dig] = struct{}{}
    91  		}
    92  		return m
    93  	}
    94  	return reflect.DeepEqual(flatten(d), flatten(other))
    95  }
    96  
    97  type fetchCall struct {
    98  	fetcher *fetcherMock
    99  	*mock.Call
   100  }
   101  
   102  func (fc *fetchCall) expectingEndorsers(orgs ...string) *fetchCall {
   103  	if fc.fetcher.expectedEndorsers == nil {
   104  		fc.fetcher.expectedEndorsers = make(map[string]struct{})
   105  	}
   106  	for _, org := range orgs {
   107  		sID := &mspproto.SerializedIdentity{Mspid: org, IdBytes: []byte(fmt.Sprintf("p0%s", org))}
   108  		b, _ := pb.Marshal(sID)
   109  		fc.fetcher.expectedEndorsers[string(b)] = struct{}{}
   110  	}
   111  
   112  	return fc
   113  }
   114  
   115  func (fc *fetchCall) expectingDigests(digests []privdatacommon.DigKey) *fetchCall {
   116  	fc.fetcher.expectedDigests = digests
   117  	return fc
   118  }
   119  
   120  func (fc *fetchCall) Return(returnArguments ...interface{}) *mock.Call {
   121  
   122  	return fc.Call.Return(returnArguments...)
   123  }
   124  
   125  type fetcherMock struct {
   126  	t *testing.T
   127  	mock.Mock
   128  	expectedDigests   []privdatacommon.DigKey
   129  	expectedEndorsers map[string]struct{}
   130  }
   131  
   132  func (f *fetcherMock) On(methodName string, arguments ...interface{}) *fetchCall {
   133  	return &fetchCall{
   134  		fetcher: f,
   135  		Call:    f.Mock.On(methodName, arguments...),
   136  	}
   137  }
   138  
   139  func (f *fetcherMock) fetch(dig2src dig2sources) (*privdatacommon.FetchedPvtDataContainer, error) {
   140  	uniqueEndorsements := make(map[string]interface{})
   141  	for _, endorsements := range dig2src {
   142  		for _, endorsement := range endorsements {
   143  			_, exists := f.expectedEndorsers[string(endorsement.Endorser)]
   144  			if !exists {
   145  				f.t.Fatalf("Encountered a non-expected endorser: %s", string(endorsement.Endorser))
   146  			}
   147  			uniqueEndorsements[string(endorsement.Endorser)] = struct{}{}
   148  		}
   149  	}
   150  	assert.True(f.t, digests(f.expectedDigests).Equal(digests(dig2src.keys())))
   151  	assert.Equal(f.t, len(f.expectedEndorsers), len(uniqueEndorsements))
   152  	args := f.Called(dig2src)
   153  	if args.Get(1) == nil {
   154  		return args.Get(0).(*privdatacommon.FetchedPvtDataContainer), nil
   155  	}
   156  	return nil, args.Get(1).(error)
   157  }
   158  
   159  type testTransientStore struct {
   160  	storeProvider transientstore.StoreProvider
   161  	store         *transientstore.Store
   162  	tempdir       string
   163  }
   164  
   165  func newTransientStore(t *testing.T) *testTransientStore {
   166  	s := &testTransientStore{}
   167  	var err error
   168  	s.tempdir, err = ioutil.TempDir("", "ts")
   169  	if err != nil {
   170  		t.Fatalf("Failed to create test directory, got err %s", err)
   171  		return s
   172  	}
   173  	s.storeProvider, err = transientstore.NewStoreProvider(s.tempdir)
   174  	if err != nil {
   175  		t.Fatalf("Failed to open store, got err %s", err)
   176  		return s
   177  	}
   178  	s.store, err = s.storeProvider.OpenStore("testchannelid")
   179  	if err != nil {
   180  		t.Fatalf("Failed to open store, got err %s", err)
   181  		return s
   182  	}
   183  	return s
   184  }
   185  
   186  func (s *testTransientStore) tearDown() {
   187  	s.storeProvider.Close()
   188  	os.RemoveAll(s.tempdir)
   189  }
   190  
   191  func (s *testTransientStore) Persist(txid string, blockHeight uint64,
   192  	privateSimulationResultsWithConfig *tspb.TxPvtReadWriteSetWithConfigInfo) error {
   193  	return s.store.Persist(txid, blockHeight, privateSimulationResultsWithConfig)
   194  }
   195  
   196  func (s *testTransientStore) GetTxPvtRWSetByTxid(txid string, filter ledger.PvtNsCollFilter) (RWSetScanner, error) {
   197  	return s.store.GetTxPvtRWSetByTxid(txid, filter)
   198  }
   199  
   200  func createcollectionStore(expectedSignedData protoutil.SignedData) *collectionStore {
   201  	return &collectionStore{
   202  		expectedSignedData: expectedSignedData,
   203  		policies:           make(map[collectionAccessPolicy]CollectionCriteria),
   204  		store:              make(map[CollectionCriteria]collectionAccessPolicy),
   205  	}
   206  }
   207  
   208  type collectionStore struct {
   209  	expectedSignedData protoutil.SignedData
   210  	acceptsAll         bool
   211  	acceptsNone        bool
   212  	lenient            bool
   213  	mspIdentifier      string
   214  	store              map[CollectionCriteria]collectionAccessPolicy
   215  	policies           map[collectionAccessPolicy]CollectionCriteria
   216  }
   217  
   218  func (cs *collectionStore) thatAcceptsAll() *collectionStore {
   219  	cs.acceptsAll = true
   220  	return cs
   221  }
   222  
   223  func (cs *collectionStore) thatAcceptsNone() *collectionStore {
   224  	cs.acceptsNone = true
   225  	return cs
   226  }
   227  
   228  func (cs *collectionStore) thatAccepts(cc CollectionCriteria) *collectionStore {
   229  	sp := collectionAccessPolicy{
   230  		cs: cs,
   231  		n:  util.RandomUInt64(),
   232  	}
   233  	cs.store[cc] = sp
   234  	cs.policies[sp] = cc
   235  	return cs
   236  }
   237  
   238  func (cs *collectionStore) withMSPIdentity(identifier string) *collectionStore {
   239  	cs.mspIdentifier = identifier
   240  	return cs
   241  }
   242  
   243  func (cs *collectionStore) RetrieveCollectionAccessPolicy(cc privdata.CollectionCriteria) (privdata.CollectionAccessPolicy, error) {
   244  	if sp, exists := cs.store[fromCollectionCriteria(cc)]; exists {
   245  		return &sp, nil
   246  	}
   247  	if cs.acceptsAll || cs.acceptsNone || cs.lenient {
   248  		return &collectionAccessPolicy{
   249  			cs: cs,
   250  			n:  util.RandomUInt64(),
   251  		}, nil
   252  	}
   253  	return nil, privdata.NoSuchCollectionError{}
   254  }
   255  
   256  func (cs *collectionStore) RetrieveCollection(privdata.CollectionCriteria) (privdata.Collection, error) {
   257  	panic("implement me")
   258  }
   259  
   260  func (cs *collectionStore) RetrieveCollectionConfig(cc privdata.CollectionCriteria) (*peer.StaticCollectionConfig, error) {
   261  	mspIdentifier := "different-org"
   262  	if _, exists := cs.store[fromCollectionCriteria(cc)]; exists || cs.acceptsAll {
   263  		mspIdentifier = cs.mspIdentifier
   264  	}
   265  	return &peer.StaticCollectionConfig{
   266  		Name:           cc.Collection,
   267  		MemberOnlyRead: true,
   268  		MemberOrgsPolicy: &peer.CollectionPolicyConfig{
   269  			Payload: &peer.CollectionPolicyConfig_SignaturePolicy{
   270  				SignaturePolicy: &common.SignaturePolicyEnvelope{
   271  					Rule: &common.SignaturePolicy{
   272  						Type: &common.SignaturePolicy_SignedBy{
   273  							SignedBy: 0,
   274  						},
   275  					},
   276  					Identities: []*mspproto.MSPPrincipal{
   277  						{
   278  							PrincipalClassification: mspproto.MSPPrincipal_ROLE,
   279  							Principal: protoutil.MarshalOrPanic(&mspproto.MSPRole{
   280  								MspIdentifier: mspIdentifier,
   281  								Role:          mspproto.MSPRole_MEMBER,
   282  							}),
   283  						},
   284  					},
   285  				},
   286  			},
   287  		},
   288  	}, nil
   289  }
   290  
   291  func (cs *collectionStore) RetrieveReadWritePermission(cc privdata.CollectionCriteria, sp *peer.SignedProposal, qe ledger.QueryExecutor) (bool, bool, error) {
   292  	panic("implement me")
   293  }
   294  
   295  func (cs *collectionStore) RetrieveCollectionConfigPackage(cc privdata.CollectionCriteria) (*peer.CollectionConfigPackage, error) {
   296  	return &peer.CollectionConfigPackage{
   297  		Config: []*peer.CollectionConfig{
   298  			{
   299  				Payload: &peer.CollectionConfig_StaticCollectionConfig{
   300  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   301  						Name:              cc.Collection,
   302  						MaximumPeerCount:  1,
   303  						RequiredPeerCount: 1,
   304  					},
   305  				},
   306  			},
   307  		},
   308  	}, nil
   309  }
   310  
   311  func (cs *collectionStore) RetrieveCollectionPersistenceConfigs(cc privdata.CollectionCriteria) (privdata.CollectionPersistenceConfigs, error) {
   312  	panic("implement me")
   313  }
   314  
   315  func (cs *collectionStore) AccessFilter(channelName string, collectionPolicyConfig *peer.CollectionPolicyConfig) (privdata.Filter, error) {
   316  	panic("implement me")
   317  }
   318  
   319  type collectionAccessPolicy struct {
   320  	cs *collectionStore
   321  	n  uint64
   322  }
   323  
   324  func (cap *collectionAccessPolicy) MemberOrgs() []string {
   325  	return []string{"org0", "org1"}
   326  }
   327  
   328  func (cap *collectionAccessPolicy) RequiredPeerCount() int {
   329  	return 1
   330  }
   331  
   332  func (cap *collectionAccessPolicy) MaximumPeerCount() int {
   333  	return 2
   334  }
   335  
   336  func (cap *collectionAccessPolicy) IsMemberOnlyRead() bool {
   337  	return false
   338  }
   339  
   340  func (cap *collectionAccessPolicy) IsMemberOnlyWrite() bool {
   341  	return false
   342  }
   343  
   344  func (cap *collectionAccessPolicy) AccessFilter() privdata.Filter {
   345  	return func(sd protoutil.SignedData) bool {
   346  		that, _ := asn1.Marshal(sd)
   347  		this, _ := asn1.Marshal(cap.cs.expectedSignedData)
   348  		if hex.EncodeToString(that) != hex.EncodeToString(this) {
   349  			panic(fmt.Errorf("self signed data passed isn't equal to expected:%v, %v", sd, cap.cs.expectedSignedData))
   350  		}
   351  
   352  		if cap.cs.acceptsNone {
   353  			return false
   354  		} else if cap.cs.acceptsAll {
   355  			return true
   356  		}
   357  
   358  		_, exists := cap.cs.policies[*cap]
   359  		return exists
   360  	}
   361  }
   362  
   363  func TestPvtDataCollections_FailOnEmptyPayload(t *testing.T) {
   364  	collection := &util.PvtDataCollections{
   365  		&ledger.TxPvtData{
   366  			SeqInBlock: uint64(1),
   367  			WriteSet: &rwset.TxPvtReadWriteSet{
   368  				DataModel: rwset.TxReadWriteSet_KV,
   369  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   370  					{
   371  						Namespace: "ns1",
   372  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   373  							{
   374  								CollectionName: "secretCollection",
   375  								Rwset:          []byte{1, 2, 3, 4, 5, 6, 7},
   376  							},
   377  						},
   378  					},
   379  				},
   380  			},
   381  		},
   382  
   383  		nil,
   384  	}
   385  
   386  	_, err := collection.Marshal()
   387  	assertion := assert.New(t)
   388  	assertion.Error(err, "Expected to fail since second item has nil payload")
   389  	assertion.Equal("Mallformed private data payload, rwset index 1 is nil", fmt.Sprintf("%s", err))
   390  }
   391  
   392  func TestPvtDataCollections_FailMarshalingWriteSet(t *testing.T) {
   393  	collection := &util.PvtDataCollections{
   394  		&ledger.TxPvtData{
   395  			SeqInBlock: uint64(1),
   396  			WriteSet:   nil,
   397  		},
   398  	}
   399  
   400  	_, err := collection.Marshal()
   401  	assertion := assert.New(t)
   402  	assertion.Error(err, "Expected to fail since first item has nil writeset")
   403  	assertion.Contains(fmt.Sprintf("%s", err), "Could not marshal private rwset index 0")
   404  }
   405  
   406  func TestPvtDataCollections_Marshal(t *testing.T) {
   407  	collection := &util.PvtDataCollections{
   408  		&ledger.TxPvtData{
   409  			SeqInBlock: uint64(1),
   410  			WriteSet: &rwset.TxPvtReadWriteSet{
   411  				DataModel: rwset.TxReadWriteSet_KV,
   412  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   413  					{
   414  						Namespace: "ns1",
   415  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   416  							{
   417  								CollectionName: "secretCollection",
   418  								Rwset:          []byte{1, 2, 3, 4, 5, 6, 7},
   419  							},
   420  						},
   421  					},
   422  				},
   423  			},
   424  		},
   425  
   426  		&ledger.TxPvtData{
   427  			SeqInBlock: uint64(2),
   428  			WriteSet: &rwset.TxPvtReadWriteSet{
   429  				DataModel: rwset.TxReadWriteSet_KV,
   430  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   431  					{
   432  						Namespace: "ns1",
   433  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   434  							{
   435  								CollectionName: "secretCollection",
   436  								Rwset:          []byte{42, 42, 42, 42, 42, 42, 42},
   437  							},
   438  						},
   439  					},
   440  					{
   441  						Namespace: "ns2",
   442  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   443  							{
   444  								CollectionName: "otherCollection",
   445  								Rwset:          []byte{10, 9, 8, 7, 6, 5, 4, 3, 2, 1},
   446  							},
   447  						},
   448  					},
   449  				},
   450  			},
   451  		},
   452  	}
   453  
   454  	bytes, err := collection.Marshal()
   455  
   456  	assertion := assert.New(t)
   457  	assertion.NoError(err)
   458  	assertion.NotNil(bytes)
   459  	assertion.Equal(2, len(bytes))
   460  }
   461  
   462  func TestPvtDataCollections_Unmarshal(t *testing.T) {
   463  	collection := util.PvtDataCollections{
   464  		&ledger.TxPvtData{
   465  			SeqInBlock: uint64(1),
   466  			WriteSet: &rwset.TxPvtReadWriteSet{
   467  				DataModel: rwset.TxReadWriteSet_KV,
   468  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   469  					{
   470  						Namespace: "ns1",
   471  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   472  							{
   473  								CollectionName: "secretCollection",
   474  								Rwset:          []byte{1, 2, 3, 4, 5, 6, 7},
   475  							},
   476  						},
   477  					},
   478  				},
   479  			},
   480  		},
   481  	}
   482  
   483  	bytes, err := collection.Marshal()
   484  
   485  	assertion := assert.New(t)
   486  	assertion.NoError(err)
   487  	assertion.NotNil(bytes)
   488  	assertion.Equal(1, len(bytes))
   489  
   490  	var newCol util.PvtDataCollections
   491  
   492  	err = newCol.Unmarshal(bytes)
   493  	assertion.NoError(err)
   494  	assertion.Equal(1, len(newCol))
   495  	assertion.Equal(newCol[0].SeqInBlock, collection[0].SeqInBlock)
   496  	assertion.True(pb.Equal(newCol[0].WriteSet, collection[0].WriteSet))
   497  }
   498  
   499  type rwsTriplet struct {
   500  	namespace  string
   501  	collection string
   502  	rwset      string
   503  }
   504  
   505  func flattenTxPvtDataMap(pd ledger.TxPvtDataMap) map[uint64]map[rwsTriplet]struct{} {
   506  	m := make(map[uint64]map[rwsTriplet]struct{})
   507  	for seqInBlock, namespaces := range pd {
   508  		triplets := make(map[rwsTriplet]struct{})
   509  		for _, namespace := range namespaces.WriteSet.NsPvtRwset {
   510  			for _, col := range namespace.CollectionPvtRwset {
   511  				triplets[rwsTriplet{
   512  					namespace:  namespace.Namespace,
   513  					collection: col.CollectionName,
   514  					rwset:      hex.EncodeToString(col.Rwset),
   515  				}] = struct{}{}
   516  			}
   517  		}
   518  		m[seqInBlock] = triplets
   519  	}
   520  	return m
   521  }
   522  
   523  var expectedCommittedPrivateData1 = map[uint64]*ledger.TxPvtData{
   524  	0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{
   525  		DataModel: rwset.TxReadWriteSet_KV,
   526  		NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   527  			{
   528  				Namespace: "ns1",
   529  				CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   530  					{
   531  						CollectionName: "c1",
   532  						Rwset:          []byte("rws-pre-image"),
   533  					},
   534  					{
   535  						CollectionName: "c2",
   536  						Rwset:          []byte("rws-pre-image"),
   537  					},
   538  				},
   539  			},
   540  		},
   541  	}},
   542  	1: {SeqInBlock: 1, WriteSet: &rwset.TxPvtReadWriteSet{
   543  		DataModel: rwset.TxReadWriteSet_KV,
   544  		NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   545  			{
   546  				Namespace: "ns2",
   547  				CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   548  					{
   549  						CollectionName: "c1",
   550  						Rwset:          []byte("rws-pre-image"),
   551  					},
   552  				},
   553  			},
   554  		},
   555  	}},
   556  }
   557  
   558  var expectedCommittedPrivateData2 = map[uint64]*ledger.TxPvtData{
   559  	0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{
   560  		DataModel: rwset.TxReadWriteSet_KV,
   561  		NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   562  			{
   563  				Namespace: "ns3",
   564  				CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   565  					{
   566  						CollectionName: "c3",
   567  						Rwset:          []byte("rws-pre-image"),
   568  					},
   569  				},
   570  			},
   571  		},
   572  	}},
   573  }
   574  
   575  var expectedCommittedPrivateData3 = map[uint64]*ledger.TxPvtData{}
   576  
   577  func TestCoordinatorStoreInvalidBlock(t *testing.T) {
   578  	err := msptesttools.LoadMSPSetupForTesting()
   579  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
   580  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
   581  	serializedID, err := identity.Serialize()
   582  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
   583  	data := []byte{1, 2, 3}
   584  	signature, err := identity.Sign(data)
   585  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
   586  	peerSelfSignedData := protoutil.SignedData{
   587  		Identity:  serializedID,
   588  		Signature: signature,
   589  		Data:      data,
   590  	}
   591  
   592  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
   593  
   594  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
   595  	committer := &mocks.Committer{}
   596  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   597  		t.Fatal("Shouldn't have committed")
   598  	}).Return(nil)
   599  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
   600  
   601  	store := newTransientStore(t)
   602  	defer store.tearDown()
   603  
   604  	assertPurged := func(txns ...string) {
   605  		for _, txn := range txns {
   606  			iterator, err := store.GetTxPvtRWSetByTxid(txn, nil)
   607  			if err != nil {
   608  				t.Fatalf("Failed iterating, got err %s", err)
   609  				iterator.Close()
   610  				return
   611  			}
   612  			res, err := iterator.Next()
   613  			if err != nil {
   614  				t.Fatalf("Failed iterating, got err %s", err)
   615  				iterator.Close()
   616  				return
   617  			}
   618  			assert.Nil(t, res)
   619  			iterator.Close()
   620  		}
   621  	}
   622  	fetcher := &fetcherMock{t: t}
   623  	pdFactory := &pvtDataFactory{}
   624  	bf := &blockFactory{
   625  		channelID: "testchannelid",
   626  	}
   627  
   628  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
   629  		return mgmt.GetManagerForChain("testchannelid")
   630  	})
   631  	block := bf.withoutMetadata().create()
   632  	// Scenario I: Block we got doesn't have any metadata with it
   633  	pvtData := pdFactory.create()
   634  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
   635  	capabilityProvider := &capabilitymock.CapabilityProvider{}
   636  	appCapability := &capabilitymock.AppCapabilities{}
   637  	capabilityProvider.On("Capabilities").Return(appCapability)
   638  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
   639  	coordinator := NewCoordinator(Support{
   640  		ChainID:            "testchannelid",
   641  		CollectionStore:    cs,
   642  		Committer:          committer,
   643  		Fetcher:            fetcher,
   644  		Validator:          &validatorMock{},
   645  		CapabilityProvider: capabilityProvider,
   646  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   647  	err = coordinator.StoreBlock(block, pvtData)
   648  	assert.Error(t, err)
   649  	assert.Contains(t, err.Error(), "Block.Metadata is nil or Block.Metadata lacks a Tx filter bitmap")
   650  
   651  	// Scenario II: Validator has an error while validating the block
   652  	block = bf.create()
   653  	pvtData = pdFactory.create()
   654  	coordinator = NewCoordinator(Support{
   655  		ChainID:            "testchannelid",
   656  		CollectionStore:    cs,
   657  		Committer:          committer,
   658  		Fetcher:            fetcher,
   659  		Validator:          &validatorMock{fmt.Errorf("failed validating block")},
   660  		CapabilityProvider: capabilityProvider,
   661  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   662  	err = coordinator.StoreBlock(block, pvtData)
   663  	assert.Error(t, err)
   664  	assert.Contains(t, err.Error(), "failed validating block")
   665  
   666  	// Scenario III: Block we got contains an inadequate length of Tx filter in the metadata
   667  	block = bf.withMetadataSize(100).create()
   668  	pvtData = pdFactory.create()
   669  	coordinator = NewCoordinator(Support{
   670  		ChainID:            "testchannelid",
   671  		CollectionStore:    cs,
   672  		Committer:          committer,
   673  		Fetcher:            fetcher,
   674  		Validator:          &validatorMock{},
   675  		CapabilityProvider: capabilityProvider,
   676  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   677  	err = coordinator.StoreBlock(block, pvtData)
   678  	assert.Error(t, err)
   679  	assert.Contains(t, err.Error(), "block data size")
   680  	assert.Contains(t, err.Error(), "is different from Tx filter size")
   681  
   682  	// Scenario IV: The second transaction in the block we got is invalid, and we have no private data for that.
   683  	// As the StorePvtDataOfInvalidTx is set of false, if the coordinator would try to fetch private data, the
   684  	// test would fall because we haven't defined the mock operations for the transientstore (or for gossip)
   685  	// in this test.
   686  	var commitHappened bool
   687  	assertCommitHappened := func() {
   688  		assert.True(t, commitHappened)
   689  		commitHappened = false
   690  	}
   691  	digKeys := []privdatacommon.DigKey{
   692  		{
   693  			TxId:       "tx2",
   694  			Namespace:  "ns2",
   695  			Collection: "c1",
   696  			BlockSeq:   1,
   697  			SeqInBlock: 1,
   698  		},
   699  	}
   700  	fetcher = &fetcherMock{t: t}
   701  	fetcher.On("fetch", mock.Anything).expectingDigests(digKeys).expectingEndorsers(identity.GetMSPIdentifier()).Return(&privdatacommon.FetchedPvtDataContainer{
   702  		AvailableElements: nil,
   703  	}, nil)
   704  	committer = &mocks.Committer{}
   705  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   706  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
   707  		commitHappened = true
   708  		// Only the first transaction's private data is passed to the ledger
   709  		assert.Len(t, privateDataPassed2Ledger, 1)
   710  		assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock))
   711  		// The private data passed to the ledger contains "ns1" and has 2 collections in it
   712  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1)
   713  		assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace)
   714  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2)
   715  	}).Return(nil)
   716  	block = bf.withInvalidTxns(1).AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create()
   717  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create()
   718  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
   719  
   720  	capabilityProvider = &capabilitymock.CapabilityProvider{}
   721  	appCapability = &capabilitymock.AppCapabilities{}
   722  	capabilityProvider.On("Capabilities").Return(appCapability)
   723  	appCapability.On("StorePvtDataOfInvalidTx").Return(false)
   724  	coordinator = NewCoordinator(Support{
   725  		ChainID:            "testchannelid",
   726  		CollectionStore:    cs,
   727  		Committer:          committer,
   728  		Fetcher:            fetcher,
   729  		Validator:          &validatorMock{},
   730  		CapabilityProvider: capabilityProvider,
   731  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   732  	err = coordinator.StoreBlock(block, pvtData)
   733  	assert.NoError(t, err)
   734  	assertCommitHappened()
   735  	// Ensure the 2nd transaction which is invalid and wasn't committed - is still purged.
   736  	// This is so that if we get a transaction via dissemination from an endorser, we purge it
   737  	// when its block comes.
   738  	assertPurged("tx1", "tx2")
   739  
   740  	// Scenario V: The second transaction in the block we got is invalid, and we have no private
   741  	// data for that in the transient store. As we have set StorePvtDataOfInvalidTx to true and
   742  	// configured the coordinator to skip pulling pvtData of invalid transactions from other peers,
   743  	// it should not store the pvtData of invalid transaction in the ledger instead a missing entry.
   744  	testConfig.SkipPullingInvalidTransactions = true
   745  	assertCommitHappened = func() {
   746  		assert.True(t, commitHappened)
   747  		commitHappened = false
   748  	}
   749  	committer = &mocks.Committer{}
   750  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   751  		blockAndPvtData := args.Get(0).(*ledger.BlockAndPvtData)
   752  		commitHappened = true
   753  		// Only the first transaction's private data is passed to the ledger
   754  		privateDataPassed2Ledger := blockAndPvtData.PvtData
   755  		assert.Len(t, privateDataPassed2Ledger, 1)
   756  		assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock))
   757  		// The private data passed to the ledger contains "ns1" and has 2 collections in it
   758  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1)
   759  		assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace)
   760  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2)
   761  
   762  		missingPrivateDataPassed2Ledger := blockAndPvtData.MissingPvtData
   763  		assert.Len(t, missingPrivateDataPassed2Ledger, 1)
   764  		assert.Len(t, missingPrivateDataPassed2Ledger[1], 1)
   765  		assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].Namespace, "ns2")
   766  		assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].Collection, "c1")
   767  		assert.Equal(t, missingPrivateDataPassed2Ledger[1][0].IsEligible, true)
   768  
   769  		commitOpts := args.Get(1).(*ledger.CommitOptions)
   770  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
   771  		assert.Equal(t, expectedCommitOpts, commitOpts)
   772  	}).Return(nil)
   773  
   774  	block = bf.withInvalidTxns(1).AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create()
   775  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create()
   776  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
   777  	capabilityProvider = &capabilitymock.CapabilityProvider{}
   778  	appCapability = &capabilitymock.AppCapabilities{}
   779  	capabilityProvider.On("Capabilities").Return(appCapability)
   780  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
   781  	digKeys = []privdatacommon.DigKey{}
   782  	fetcher = &fetcherMock{t: t}
   783  	fetcher.On("fetch", mock.Anything).expectingDigests(digKeys).Return(&privdatacommon.FetchedPvtDataContainer{
   784  		AvailableElements: nil,
   785  	}, nil)
   786  	coordinator = NewCoordinator(Support{
   787  		ChainID:            "testchannelid",
   788  		CollectionStore:    cs,
   789  		Committer:          committer,
   790  		Fetcher:            fetcher,
   791  		Validator:          &validatorMock{},
   792  		CapabilityProvider: capabilityProvider,
   793  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   794  	err = coordinator.StoreBlock(block, pvtData)
   795  	assert.NoError(t, err)
   796  	assertCommitHappened()
   797  	assertPurged("tx1", "tx2")
   798  
   799  	// Scenario VI: The second transaction in the block we got is invalid. As we have set the
   800  	// StorePvtDataOfInvalidTx to true and configured the coordinator to pull pvtData of invalid
   801  	// transactions, it should store the pvtData of invalid transactions in the ledger.
   802  	testConfig.SkipPullingInvalidTransactions = false
   803  	committer = &mocks.Committer{}
   804  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   805  		blockAndPvtData := args.Get(0).(*ledger.BlockAndPvtData)
   806  		commitHappened = true
   807  		// pvtData of both transactions must be present though the second transaction
   808  		// is invalid.
   809  		privateDataPassed2Ledger := blockAndPvtData.PvtData
   810  		assert.Len(t, privateDataPassed2Ledger, 2)
   811  		assert.Equal(t, 0, int(privateDataPassed2Ledger[0].SeqInBlock))
   812  		assert.Equal(t, 1, int(privateDataPassed2Ledger[1].SeqInBlock))
   813  		// The private data passed to the ledger for tx1 contains "ns1" and has 2 collections in it
   814  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset, 1)
   815  		assert.Equal(t, "ns1", privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].Namespace)
   816  		assert.Len(t, privateDataPassed2Ledger[0].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 2)
   817  		// The private data passed to the ledger for tx2 contains "ns2" and has 1 collection in it
   818  		assert.Len(t, privateDataPassed2Ledger[1].WriteSet.NsPvtRwset, 1)
   819  		assert.Equal(t, "ns2", privateDataPassed2Ledger[1].WriteSet.NsPvtRwset[0].Namespace)
   820  		assert.Len(t, privateDataPassed2Ledger[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset, 1)
   821  
   822  		missingPrivateDataPassed2Ledger := blockAndPvtData.MissingPvtData
   823  		assert.Len(t, missingPrivateDataPassed2Ledger, 0)
   824  
   825  		commitOpts := args.Get(1).(*ledger.CommitOptions)
   826  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
   827  		assert.Equal(t, expectedCommitOpts, commitOpts)
   828  	}).Return(nil)
   829  
   830  	fetcher = &fetcherMock{t: t}
   831  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
   832  		{
   833  			TxId: "tx2", Namespace: "ns2", Collection: "c1", BlockSeq: 1, SeqInBlock: 1,
   834  		},
   835  	}).Return(&privdatacommon.FetchedPvtDataContainer{
   836  		AvailableElements: []*proto.PvtDataElement{
   837  			{
   838  				Digest: &proto.PvtDataDigest{
   839  					SeqInBlock: 1,
   840  					BlockSeq:   1,
   841  					Collection: "c1",
   842  					Namespace:  "ns2",
   843  					TxId:       "tx2",
   844  				},
   845  				Payload: [][]byte{[]byte("rws-pre-image")},
   846  			},
   847  		},
   848  	}, nil)
   849  
   850  	block = bf.withInvalidTxns(1).AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2").
   851  		AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create()
   852  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").create()
   853  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
   854  	coordinator = NewCoordinator(Support{
   855  		ChainID:            "testchannelid",
   856  		CollectionStore:    cs,
   857  		Committer:          committer,
   858  		Fetcher:            fetcher,
   859  		Validator:          &validatorMock{},
   860  		CapabilityProvider: capabilityProvider,
   861  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   862  	err = coordinator.StoreBlock(block, pvtData)
   863  	assert.NoError(t, err)
   864  	assertCommitHappened()
   865  	assertPurged("tx1", "tx2")
   866  
   867  	// Scenario VII: Block doesn't contain a header
   868  	block.Header = nil
   869  	err = coordinator.StoreBlock(block, pvtData)
   870  	assert.Error(t, err)
   871  	assert.Contains(t, err.Error(), "Block header is nil")
   872  
   873  	// Scenario VIII: Block doesn't contain Data
   874  	block.Data = nil
   875  	err = coordinator.StoreBlock(block, pvtData)
   876  	assert.Error(t, err)
   877  	assert.Contains(t, err.Error(), "Block data is empty")
   878  }
   879  
   880  func TestCoordinatorToFilterOutPvtRWSetsWithWrongHash(t *testing.T) {
   881  	/*
   882  		Test case, where peer receives new block for commit
   883  		it has ns1:c1 in transient store, while it has wrong
   884  		hash, hence it will fetch ns1:c1 from other peers
   885  	*/
   886  	err := msptesttools.LoadMSPSetupForTesting()
   887  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
   888  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
   889  	serializedID, err := identity.Serialize()
   890  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
   891  	data := []byte{1, 2, 3}
   892  	signature, err := identity.Sign(data)
   893  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
   894  	peerSelfSignedData := protoutil.SignedData{
   895  		Identity:  serializedID,
   896  		Signature: signature,
   897  		Data:      data,
   898  	}
   899  
   900  	expectedPvtData := map[uint64]*ledger.TxPvtData{
   901  		0: {SeqInBlock: 0, WriteSet: &rwset.TxPvtReadWriteSet{
   902  			DataModel: rwset.TxReadWriteSet_KV,
   903  			NsPvtRwset: []*rwset.NsPvtReadWriteSet{
   904  				{
   905  					Namespace: "ns1",
   906  					CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
   907  						{
   908  							CollectionName: "c1",
   909  							Rwset:          []byte("rws-original"),
   910  						},
   911  					},
   912  				},
   913  			},
   914  		}},
   915  	}
   916  
   917  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
   918  	committer := &mocks.Committer{}
   919  
   920  	store := newTransientStore(t)
   921  	defer store.tearDown()
   922  
   923  	assertPurged := func(txns ...string) {
   924  		for _, txn := range txns {
   925  			iterator, err := store.GetTxPvtRWSetByTxid(txn, nil)
   926  			if err != nil {
   927  				t.Fatalf("Failed iterating, got err %s", err)
   928  				iterator.Close()
   929  				return
   930  			}
   931  			res, err := iterator.Next()
   932  			if err != nil {
   933  				t.Fatalf("Failed iterating, got err %s", err)
   934  				iterator.Close()
   935  				return
   936  			}
   937  			assert.Nil(t, res)
   938  			iterator.Close()
   939  		}
   940  	}
   941  
   942  	fetcher := &fetcherMock{t: t}
   943  
   944  	var commitHappened bool
   945  
   946  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   947  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
   948  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
   949  			flattenTxPvtDataMap(expectedPvtData)))
   950  		commitHappened = true
   951  
   952  		commitOpts := args.Get(1).(*ledger.CommitOptions)
   953  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
   954  		assert.Equal(t, expectedCommitOpts, commitOpts)
   955  	}).Return(nil)
   956  
   957  	hash := util2.ComputeSHA256([]byte("rws-original"))
   958  	bf := &blockFactory{
   959  		channelID: "testchannelid",
   960  	}
   961  
   962  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
   963  		return mgmt.GetManagerForChain("testchannelid")
   964  	})
   965  
   966  	block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1").create()
   967  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
   968  
   969  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
   970  
   971  	capabilityProvider := &capabilitymock.CapabilityProvider{}
   972  	appCapability := &capabilitymock.AppCapabilities{}
   973  	capabilityProvider.On("Capabilities").Return(appCapability)
   974  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
   975  	coordinator := NewCoordinator(Support{
   976  		ChainID:            "testchannelid",
   977  		CollectionStore:    cs,
   978  		Committer:          committer,
   979  		Fetcher:            fetcher,
   980  		Validator:          &validatorMock{},
   981  		CapabilityProvider: capabilityProvider,
   982  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
   983  
   984  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
   985  		{
   986  			TxId: "tx1", Namespace: "ns1", Collection: "c1", BlockSeq: 1,
   987  		},
   988  	}).Return(&privdatacommon.FetchedPvtDataContainer{
   989  		AvailableElements: []*proto.PvtDataElement{
   990  			{
   991  				Digest: &proto.PvtDataDigest{
   992  					BlockSeq:   1,
   993  					Collection: "c1",
   994  					Namespace:  "ns1",
   995  					TxId:       "tx1",
   996  				},
   997  				Payload: [][]byte{[]byte("rws-original")},
   998  			},
   999  		},
  1000  	}, nil)
  1001  
  1002  	coordinator.StoreBlock(block, nil)
  1003  	// Assert blocks was eventually committed
  1004  	assert.True(t, commitHappened)
  1005  
  1006  	// Assert transaction has been purged
  1007  	assertPurged("tx1")
  1008  }
  1009  
  1010  func TestCoordinatorStoreBlock(t *testing.T) {
  1011  	err := msptesttools.LoadMSPSetupForTesting()
  1012  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1013  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1014  	serializedID, err := identity.Serialize()
  1015  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1016  	data := []byte{1, 2, 3}
  1017  	signature, err := identity.Sign(data)
  1018  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1019  	peerSelfSignedData := protoutil.SignedData{
  1020  		Identity:  serializedID,
  1021  		Signature: signature,
  1022  		Data:      data,
  1023  	}
  1024  	// Green path test, all private data should be obtained successfully
  1025  
  1026  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
  1027  
  1028  	var commitHappened bool
  1029  	assertCommitHappened := func() {
  1030  		assert.True(t, commitHappened)
  1031  		commitHappened = false
  1032  	}
  1033  	committer := &mocks.Committer{}
  1034  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1035  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
  1036  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
  1037  			flattenTxPvtDataMap(expectedCommittedPrivateData1)))
  1038  		commitHappened = true
  1039  
  1040  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1041  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1042  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1043  	}).Return(nil)
  1044  
  1045  	store := newTransientStore(t)
  1046  	defer store.tearDown()
  1047  
  1048  	assertPurged := func(txns ...string) {
  1049  		for _, txn := range txns {
  1050  			iterator, err := store.GetTxPvtRWSetByTxid(txn, nil)
  1051  			if err != nil {
  1052  				t.Fatalf("Failed iterating, got err %s", err)
  1053  				iterator.Close()
  1054  				return
  1055  			}
  1056  			res, err := iterator.Next()
  1057  			if err != nil {
  1058  				t.Fatalf("Failed iterating, got err %s", err)
  1059  				iterator.Close()
  1060  				return
  1061  			}
  1062  			assert.Nil(t, res)
  1063  			iterator.Close()
  1064  		}
  1065  	}
  1066  
  1067  	fetcher := &fetcherMock{t: t}
  1068  
  1069  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1070  	pdFactory := &pvtDataFactory{}
  1071  	bf := &blockFactory{
  1072  		channelID: "testchannelid",
  1073  	}
  1074  
  1075  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1076  		return mgmt.GetManagerForChain("testchannelid")
  1077  	})
  1078  
  1079  	block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2").
  1080  		AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create()
  1081  
  1082  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1083  
  1084  	fmt.Println("Scenario I")
  1085  	// Scenario I: Block we got has sufficient private data alongside it.
  1086  	// If the coordinator tries fetching from the transientstore, or peers it would result in panic,
  1087  	// because we didn't define yet the "On(...)" invocation of the transient store or other peers.
  1088  	pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create()
  1089  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1090  
  1091  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1092  	appCapability := &capabilitymock.AppCapabilities{}
  1093  	capabilityProvider.On("Capabilities").Return(appCapability)
  1094  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1095  	coordinator := NewCoordinator(Support{
  1096  		ChainID:            "testchannelid",
  1097  		CollectionStore:    cs,
  1098  		Committer:          committer,
  1099  		Fetcher:            fetcher,
  1100  		Validator:          &validatorMock{},
  1101  		CapabilityProvider: capabilityProvider,
  1102  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1103  	err = coordinator.StoreBlock(block, pvtData)
  1104  	assert.NoError(t, err)
  1105  	assertCommitHappened()
  1106  	assertPurged("tx1", "tx2")
  1107  
  1108  	fmt.Println("Scenario II")
  1109  	// Scenario II: Block we got doesn't have sufficient private data alongside it,
  1110  	// it is missing ns1: c2, but the data exists in the transient store
  1111  	store.Persist("tx1", 1, &tspb.TxPvtReadWriteSetWithConfigInfo{
  1112  		PvtRwset: &rwset.TxPvtReadWriteSet{
  1113  			NsPvtRwset: []*rwset.NsPvtReadWriteSet{
  1114  				{
  1115  					Namespace: "ns1",
  1116  					CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1117  						{
  1118  							CollectionName: "c2",
  1119  							Rwset:          []byte("rws-pre-image"),
  1120  						},
  1121  					},
  1122  				},
  1123  			},
  1124  		},
  1125  		CollectionConfigs: make(map[string]*peer.CollectionConfigPackage),
  1126  	})
  1127  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1").addRWSet().addNSRWSet("ns2", "c1").create()
  1128  	err = coordinator.StoreBlock(block, pvtData)
  1129  	assert.NoError(t, err)
  1130  	assertCommitHappened()
  1131  	assertPurged("tx1", "tx2")
  1132  
  1133  	fmt.Println("Scenario III")
  1134  	// Scenario III: Block doesn't have sufficient private data alongside it,
  1135  	// it is missing ns1: c2, and the data exists in the transient store,
  1136  	// but it is also missing ns2: c1, and that data doesn't exist in the transient store - but in a peer.
  1137  	// Additionally, the coordinator should pass an endorser identity of org1, but not of org2, since
  1138  	// the MemberOrgs() call doesn't return org2 but only org0 and org1.
  1139  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
  1140  		{
  1141  			TxId: "tx1", Namespace: "ns1", Collection: "c2", BlockSeq: 1,
  1142  		},
  1143  		{
  1144  			TxId: "tx2", Namespace: "ns2", Collection: "c1", BlockSeq: 1, SeqInBlock: 1,
  1145  		},
  1146  	}).Return(&privdatacommon.FetchedPvtDataContainer{
  1147  		AvailableElements: []*proto.PvtDataElement{
  1148  			{
  1149  				Digest: &proto.PvtDataDigest{
  1150  					BlockSeq:   1,
  1151  					Collection: "c2",
  1152  					Namespace:  "ns1",
  1153  					TxId:       "tx1",
  1154  				},
  1155  				Payload: [][]byte{[]byte("rws-pre-image")},
  1156  			},
  1157  			{
  1158  				Digest: &proto.PvtDataDigest{
  1159  					SeqInBlock: 1,
  1160  					BlockSeq:   1,
  1161  					Collection: "c1",
  1162  					Namespace:  "ns2",
  1163  					TxId:       "tx2",
  1164  				},
  1165  				Payload: [][]byte{[]byte("rws-pre-image")},
  1166  			},
  1167  		},
  1168  	}, nil)
  1169  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1").create()
  1170  	err = coordinator.StoreBlock(block, pvtData)
  1171  	assertPurged("tx1", "tx2")
  1172  	assert.NoError(t, err)
  1173  	assertCommitHappened()
  1174  
  1175  	fmt.Println("Scenario IV")
  1176  	// Scenario IV: Block came with more than sufficient private data alongside it, some of it is redundant.
  1177  	pvtData = pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2", "c3").
  1178  		addRWSet().addNSRWSet("ns2", "c1", "c3").addRWSet().addNSRWSet("ns1", "c4").create()
  1179  	err = coordinator.StoreBlock(block, pvtData)
  1180  	assertPurged("tx1", "tx2")
  1181  	assert.NoError(t, err)
  1182  	assertCommitHappened()
  1183  
  1184  	fmt.Println("Scenario V")
  1185  	// Scenario V: Block we got has private data alongside it but coordinator cannot retrieve collection access
  1186  	// policy of collections due to databse unavailability error.
  1187  	// we verify that the error propagates properly.
  1188  	mockCs := &mocks.CollectionStore{}
  1189  	mockCs.On("RetrieveCollectionConfig", mock.Anything).Return(nil, errors.New("test error"))
  1190  	coordinator = NewCoordinator(Support{
  1191  		ChainID:            "testchannelid",
  1192  		CollectionStore:    mockCs,
  1193  		Committer:          committer,
  1194  		Fetcher:            fetcher,
  1195  		Validator:          &validatorMock{},
  1196  		CapabilityProvider: capabilityProvider,
  1197  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1198  	err = coordinator.StoreBlock(block, nil)
  1199  	assert.Error(t, err)
  1200  	assert.Equal(t, "test error", err.Error())
  1201  
  1202  	fmt.Println("Scenario VI")
  1203  	// Scenario VI: Block didn't get with any private data alongside it, and the transient store
  1204  	// has some problem.
  1205  	// In this case, we should try to fetch data from peers.
  1206  	block = bf.AddTxn("tx3", "ns3", hash, "c3").create()
  1207  	fetcher = &fetcherMock{t: t}
  1208  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
  1209  		{
  1210  			TxId: "tx3", Namespace: "ns3", Collection: "c3", BlockSeq: 1,
  1211  		},
  1212  	}).Return(&privdatacommon.FetchedPvtDataContainer{
  1213  		AvailableElements: []*proto.PvtDataElement{
  1214  			{
  1215  				Digest: &proto.PvtDataDigest{
  1216  					BlockSeq:   1,
  1217  					Collection: "c3",
  1218  					Namespace:  "ns3",
  1219  					TxId:       "tx3",
  1220  				},
  1221  				Payload: [][]byte{[]byte("rws-pre-image")},
  1222  			},
  1223  		},
  1224  	}, nil)
  1225  	committer = &mocks.Committer{}
  1226  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1227  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
  1228  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
  1229  			flattenTxPvtDataMap(expectedCommittedPrivateData2)))
  1230  		commitHappened = true
  1231  
  1232  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1233  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1234  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1235  	}).Return(nil)
  1236  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1237  	coordinator = NewCoordinator(Support{
  1238  		ChainID:            "testchannelid",
  1239  		CollectionStore:    cs,
  1240  		Committer:          committer,
  1241  		Fetcher:            fetcher,
  1242  		Validator:          &validatorMock{},
  1243  		CapabilityProvider: capabilityProvider,
  1244  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1245  	err = coordinator.StoreBlock(block, nil)
  1246  	assertPurged("tx3")
  1247  	assert.NoError(t, err)
  1248  	assertCommitHappened()
  1249  
  1250  	fmt.Println("Scenario VII")
  1251  	// Scenario VII: Block contains 2 transactions, and the peer is eligible for only tx3-ns3-c3.
  1252  	// Also, the blocks comes with a private data for tx3-ns3-c3 so that the peer won't have to fetch the
  1253  	// private data from the transient store or peers, and in fact- if it attempts to fetch the data it's not eligible
  1254  	// for from the transient store or from peers - the test would fail because the Mock wasn't initialized.
  1255  	block = bf.AddTxn("tx3", "ns3", hash, "c3", "c2", "c1").AddTxn("tx1", "ns1", hash, "c1").create()
  1256  	cs = createcollectionStore(peerSelfSignedData).thatAccepts(CollectionCriteria{
  1257  		Collection: "c3",
  1258  		Namespace:  "ns3",
  1259  		Channel:    "testchannelid",
  1260  	}).withMSPIdentity(identity.GetMSPIdentifier())
  1261  	fetcher = &fetcherMock{t: t}
  1262  	committer = &mocks.Committer{}
  1263  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1264  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
  1265  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
  1266  			flattenTxPvtDataMap(expectedCommittedPrivateData2)))
  1267  		commitHappened = true
  1268  
  1269  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1270  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1271  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1272  	}).Return(nil)
  1273  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1274  	coordinator = NewCoordinator(Support{
  1275  		ChainID:            "testchannelid",
  1276  		CollectionStore:    cs,
  1277  		Committer:          committer,
  1278  		Fetcher:            fetcher,
  1279  		Validator:          &validatorMock{},
  1280  		CapabilityProvider: capabilityProvider,
  1281  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1282  
  1283  	pvtData = pdFactory.addRWSet().addNSRWSet("ns3", "c3").create()
  1284  	err = coordinator.StoreBlock(block, pvtData)
  1285  	assert.NoError(t, err)
  1286  	assertCommitHappened()
  1287  	// In any case, all transactions in the block are purged from the transient store
  1288  	assertPurged("tx3", "tx1")
  1289  }
  1290  
  1291  func TestCoordinatorStoreBlockWhenPvtDataExistInLedger(t *testing.T) {
  1292  	err := msptesttools.LoadMSPSetupForTesting()
  1293  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1294  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1295  	serializedID, err := identity.Serialize()
  1296  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1297  	data := []byte{1, 2, 3}
  1298  	signature, err := identity.Sign(data)
  1299  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1300  	peerSelfSignedData := protoutil.SignedData{
  1301  		Identity:  serializedID,
  1302  		Signature: signature,
  1303  		Data:      data,
  1304  	}
  1305  
  1306  	var commitHappened bool
  1307  	assertCommitHappened := func() {
  1308  		assert.True(t, commitHappened)
  1309  		commitHappened = false
  1310  	}
  1311  	committer := &mocks.Committer{}
  1312  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1313  		privateDataPassed2Ledger := args.Get(0).(*ledger.BlockAndPvtData).PvtData
  1314  		assert.Equal(t, ledger.TxPvtDataMap{}, privateDataPassed2Ledger)
  1315  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1316  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: true}
  1317  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1318  		commitHappened = true
  1319  	}).Return(nil)
  1320  
  1321  	fetcher := &fetcherMock{t: t}
  1322  
  1323  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1324  	pdFactory := &pvtDataFactory{}
  1325  	bf := &blockFactory{
  1326  		channelID: "testchannelid",
  1327  	}
  1328  
  1329  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1330  		return mgmt.GetManagerForChain("testchannelid")
  1331  	})
  1332  
  1333  	block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2").
  1334  		AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").create()
  1335  
  1336  	// Scenario: Block we got has been reprocessed and hence the sufficient pvtData is present
  1337  	// in the local pvtdataStore itself. The pvtData would be fetched from the local pvtdataStore.
  1338  	// If the coordinator tries fetching from the transientstore, or peers it would result in panic,
  1339  	// because we didn't define yet the "On(...)" invocation of the transient store or other peers.
  1340  	pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create()
  1341  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(true, nil)
  1342  
  1343  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1344  
  1345  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1346  	appCapability := &capabilitymock.AppCapabilities{}
  1347  	capabilityProvider.On("Capabilities").Return(appCapability)
  1348  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1349  	coordinator := NewCoordinator(Support{
  1350  		ChainID:            "testchannelid",
  1351  		CollectionStore:    nil,
  1352  		Committer:          committer,
  1353  		Fetcher:            fetcher,
  1354  		Validator:          &validatorMock{},
  1355  		CapabilityProvider: capabilityProvider,
  1356  	}, nil, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1357  	err = coordinator.StoreBlock(block, pvtData)
  1358  	assert.NoError(t, err)
  1359  	assertCommitHappened()
  1360  }
  1361  
  1362  func TestProceedWithoutPrivateData(t *testing.T) {
  1363  	// Scenario: we are missing private data (c2 in ns3) and it cannot be obtained from any peer.
  1364  	// Block needs to be committed with missing private data.
  1365  	err := msptesttools.LoadMSPSetupForTesting()
  1366  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1367  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1368  	serializedID, err := identity.Serialize()
  1369  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1370  	data := []byte{1, 2, 3}
  1371  	signature, err := identity.Sign(data)
  1372  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1373  	peerSelfSignedData := protoutil.SignedData{
  1374  		Identity:  serializedID,
  1375  		Signature: signature,
  1376  		Data:      data,
  1377  	}
  1378  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
  1379  	var commitHappened bool
  1380  	assertCommitHappened := func() {
  1381  		assert.True(t, commitHappened)
  1382  		commitHappened = false
  1383  	}
  1384  	committer := &mocks.Committer{}
  1385  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1386  		blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData)
  1387  		privateDataPassed2Ledger := blockAndPrivateData.PvtData
  1388  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
  1389  			flattenTxPvtDataMap(expectedCommittedPrivateData2)))
  1390  		missingPrivateData := blockAndPrivateData.MissingPvtData
  1391  		expectedMissingPvtData := make(ledger.TxMissingPvtDataMap)
  1392  		expectedMissingPvtData.Add(0, "ns3", "c2", true)
  1393  		assert.Equal(t, expectedMissingPvtData, missingPrivateData)
  1394  		commitHappened = true
  1395  
  1396  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1397  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1398  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1399  	}).Return(nil)
  1400  
  1401  	store := newTransientStore(t)
  1402  	defer store.tearDown()
  1403  
  1404  	assertPurged := func(txns ...string) {
  1405  		for _, txn := range txns {
  1406  			iterator, err := store.GetTxPvtRWSetByTxid(txn, nil)
  1407  			if err != nil {
  1408  				t.Fatalf("Failed iterating, got err %s", err)
  1409  				iterator.Close()
  1410  				return
  1411  			}
  1412  			res, err := iterator.Next()
  1413  			if err != nil {
  1414  				t.Fatalf("Failed iterating, got err %s", err)
  1415  				iterator.Close()
  1416  				return
  1417  			}
  1418  			assert.Nil(t, res)
  1419  			iterator.Close()
  1420  		}
  1421  	}
  1422  
  1423  	fetcher := &fetcherMock{t: t}
  1424  	// Have the peer return in response to the pull, a private data with a non matching hash
  1425  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
  1426  		{
  1427  			TxId: "tx1", Namespace: "ns3", Collection: "c2", BlockSeq: 1,
  1428  		},
  1429  	}).Return(&privdatacommon.FetchedPvtDataContainer{
  1430  		AvailableElements: []*proto.PvtDataElement{
  1431  			{
  1432  				Digest: &proto.PvtDataDigest{
  1433  					BlockSeq:   1,
  1434  					Collection: "c2",
  1435  					Namespace:  "ns3",
  1436  					TxId:       "tx1",
  1437  				},
  1438  				Payload: [][]byte{[]byte("wrong pre-image")},
  1439  			},
  1440  		},
  1441  	}, nil)
  1442  
  1443  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1444  	pdFactory := &pvtDataFactory{}
  1445  	bf := &blockFactory{
  1446  		channelID: "testchannelid",
  1447  	}
  1448  
  1449  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1450  		return mgmt.GetManagerForChain("testchannelid")
  1451  	})
  1452  
  1453  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1454  
  1455  	block := bf.AddTxn("tx1", "ns3", hash, "c3", "c2").create()
  1456  	pvtData := pdFactory.addRWSet().addNSRWSet("ns3", "c3").create()
  1457  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1458  
  1459  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1460  	appCapability := &capabilitymock.AppCapabilities{}
  1461  	capabilityProvider.On("Capabilities").Return(appCapability)
  1462  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1463  	coordinator := NewCoordinator(Support{
  1464  		ChainID:            "testchannelid",
  1465  		CollectionStore:    cs,
  1466  		Committer:          committer,
  1467  		Fetcher:            fetcher,
  1468  		Validator:          &validatorMock{},
  1469  		CapabilityProvider: capabilityProvider,
  1470  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1471  	err = coordinator.StoreBlock(block, pvtData)
  1472  	assert.NoError(t, err)
  1473  	assertCommitHappened()
  1474  	assertPurged("tx1")
  1475  }
  1476  
  1477  func TestProceedWithInEligiblePrivateData(t *testing.T) {
  1478  	// Scenario: we are missing private data (c2 in ns3) and it cannot be obtained from any peer.
  1479  	// Block needs to be committed with missing private data.
  1480  	err := msptesttools.LoadMSPSetupForTesting()
  1481  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1482  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1483  	serializedID, err := identity.Serialize()
  1484  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1485  	data := []byte{1, 2, 3}
  1486  	signature, err := identity.Sign(data)
  1487  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1488  	peerSelfSignedData := protoutil.SignedData{
  1489  		Identity:  serializedID,
  1490  		Signature: signature,
  1491  		Data:      data,
  1492  	}
  1493  
  1494  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsNone().withMSPIdentity(identity.GetMSPIdentifier())
  1495  
  1496  	var commitHappened bool
  1497  	assertCommitHappened := func() {
  1498  		assert.True(t, commitHappened)
  1499  		commitHappened = false
  1500  	}
  1501  	committer := &mocks.Committer{}
  1502  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1503  		blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData)
  1504  		privateDataPassed2Ledger := blockAndPrivateData.PvtData
  1505  		assert.True(t, reflect.DeepEqual(flattenTxPvtDataMap(privateDataPassed2Ledger),
  1506  			flattenTxPvtDataMap(expectedCommittedPrivateData3)))
  1507  		missingPrivateData := blockAndPrivateData.MissingPvtData
  1508  		expectedMissingPvtData := make(ledger.TxMissingPvtDataMap)
  1509  		expectedMissingPvtData.Add(0, "ns3", "c2", false)
  1510  		assert.Equal(t, expectedMissingPvtData, missingPrivateData)
  1511  		commitHappened = true
  1512  
  1513  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1514  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1515  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1516  	}).Return(nil)
  1517  
  1518  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1519  	bf := &blockFactory{
  1520  		channelID: "testchannelid",
  1521  	}
  1522  
  1523  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1524  		return mgmt.GetManagerForChain("testchannelid")
  1525  	})
  1526  
  1527  	block := bf.AddTxn("tx1", "ns3", hash, "c2").create()
  1528  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1529  
  1530  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1531  
  1532  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1533  	appCapability := &capabilitymock.AppCapabilities{}
  1534  	capabilityProvider.On("Capabilities").Return(appCapability)
  1535  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1536  	coordinator := NewCoordinator(Support{
  1537  		ChainID:            "testchannelid",
  1538  		CollectionStore:    cs,
  1539  		Committer:          committer,
  1540  		Fetcher:            nil,
  1541  		Validator:          &validatorMock{},
  1542  		CapabilityProvider: capabilityProvider,
  1543  	}, nil, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1544  	err = coordinator.StoreBlock(block, nil)
  1545  	assert.NoError(t, err)
  1546  	assertCommitHappened()
  1547  }
  1548  
  1549  func TestCoordinatorGetBlocks(t *testing.T) {
  1550  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1551  	err := msptesttools.LoadMSPSetupForTesting()
  1552  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1553  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1554  	serializedID, err := identity.Serialize()
  1555  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1556  	data := []byte{1, 2, 3}
  1557  	signature, err := identity.Sign(data)
  1558  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1559  	peerSelfSignedData := protoutil.SignedData{
  1560  		Identity:  serializedID,
  1561  		Signature: signature,
  1562  		Data:      data,
  1563  	}
  1564  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
  1565  	committer := &mocks.Committer{}
  1566  
  1567  	store := newTransientStore(t)
  1568  	defer store.tearDown()
  1569  
  1570  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1571  		return mgmt.GetManagerForChain("testchannelid")
  1572  	})
  1573  
  1574  	fetcher := &fetcherMock{t: t}
  1575  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1576  
  1577  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1578  	appCapability := &capabilitymock.AppCapabilities{}
  1579  	capabilityProvider.On("Capabilities").Return(appCapability)
  1580  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1581  	coordinator := NewCoordinator(Support{
  1582  		ChainID:            "testchannelid",
  1583  		CollectionStore:    cs,
  1584  		Committer:          committer,
  1585  		Fetcher:            fetcher,
  1586  		Validator:          &validatorMock{},
  1587  		CapabilityProvider: capabilityProvider,
  1588  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1589  
  1590  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1591  	bf := &blockFactory{
  1592  		channelID: "testchannelid",
  1593  	}
  1594  	block := bf.AddTxn("tx1", "ns1", hash, "c1", "c2").AddTxn("tx2", "ns2", hash, "c1").create()
  1595  
  1596  	// Green path - block and private data is returned, but the requester isn't eligible for all the private data,
  1597  	// but only to a subset of it.
  1598  	cs = createcollectionStore(peerSelfSignedData).thatAccepts(CollectionCriteria{
  1599  		Namespace:  "ns1",
  1600  		Collection: "c2",
  1601  		Channel:    "testchannelid",
  1602  	}).withMSPIdentity(identity.GetMSPIdentifier())
  1603  	committer.Mock = mock.Mock{}
  1604  	committer.On("GetPvtDataAndBlockByNum", mock.Anything).Return(&ledger.BlockAndPvtData{
  1605  		Block:   block,
  1606  		PvtData: expectedCommittedPrivateData1,
  1607  	}, nil)
  1608  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1609  	coordinator = NewCoordinator(Support{
  1610  		ChainID:            "testchannelid",
  1611  		CollectionStore:    cs,
  1612  		Committer:          committer,
  1613  		Fetcher:            fetcher,
  1614  		Validator:          &validatorMock{},
  1615  		CapabilityProvider: capabilityProvider,
  1616  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1617  	expectedPrivData := (&pvtDataFactory{}).addRWSet().addNSRWSet("ns1", "c2").create()
  1618  	block2, returnedPrivateData, err := coordinator.GetPvtDataAndBlockByNum(1, peerSelfSignedData)
  1619  	assert.NoError(t, err)
  1620  	assert.Equal(t, block, block2)
  1621  	assert.Equal(t, expectedPrivData, []*ledger.TxPvtData(returnedPrivateData))
  1622  
  1623  	// Bad path - error occurs when trying to retrieve the block and private data
  1624  	committer.Mock = mock.Mock{}
  1625  	committer.On("GetPvtDataAndBlockByNum", mock.Anything).Return(nil, errors.New("uh oh"))
  1626  	block2, returnedPrivateData, err = coordinator.GetPvtDataAndBlockByNum(1, peerSelfSignedData)
  1627  	assert.Nil(t, block2)
  1628  	assert.Empty(t, returnedPrivateData)
  1629  	assert.Error(t, err)
  1630  }
  1631  
  1632  func TestPurgeBelowHeight(t *testing.T) {
  1633  	conf := testConfig
  1634  	conf.TransientBlockRetention = 5
  1635  	peerSelfSignedData := protoutil.SignedData{}
  1636  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll()
  1637  
  1638  	committer := &mocks.Committer{}
  1639  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Return(nil)
  1640  
  1641  	store := newTransientStore(t)
  1642  	defer store.tearDown()
  1643  
  1644  	// store 9 data sets initially
  1645  	for i := 0; i < 9; i++ {
  1646  		txID := fmt.Sprintf("tx%d", i+1)
  1647  		store.Persist(txID, uint64(i), &tspb.TxPvtReadWriteSetWithConfigInfo{
  1648  			PvtRwset: &rwset.TxPvtReadWriteSet{
  1649  				NsPvtRwset: []*rwset.NsPvtReadWriteSet{
  1650  					{
  1651  						Namespace: "ns1",
  1652  						CollectionPvtRwset: []*rwset.CollectionPvtReadWriteSet{
  1653  							{
  1654  								CollectionName: "c1",
  1655  								Rwset:          []byte("rws-pre-image"),
  1656  							},
  1657  						},
  1658  					},
  1659  				},
  1660  			},
  1661  			CollectionConfigs: make(map[string]*peer.CollectionConfigPackage),
  1662  		})
  1663  	}
  1664  	assertPurged := func(purged bool) {
  1665  		numTx := 9
  1666  		if purged {
  1667  			numTx = 10
  1668  		}
  1669  		for i := 1; i <= numTx; i++ {
  1670  			txID := fmt.Sprintf("tx%d", i)
  1671  			iterator, err := store.GetTxPvtRWSetByTxid(txID, nil)
  1672  			if err != nil {
  1673  				t.Fatalf("Failed iterating, got err %s", err)
  1674  				iterator.Close()
  1675  				return
  1676  			}
  1677  			res, err := iterator.Next()
  1678  			if err != nil {
  1679  				t.Fatalf("Failed iterating, got err %s", err)
  1680  				iterator.Close()
  1681  				return
  1682  			}
  1683  			if (i < 6 || i == numTx) && purged {
  1684  				assert.Nil(t, res)
  1685  			} else {
  1686  				assert.NotNil(t, res)
  1687  			}
  1688  			iterator.Close()
  1689  		}
  1690  	}
  1691  
  1692  	fetcher := &fetcherMock{t: t}
  1693  
  1694  	bf := &blockFactory{
  1695  		channelID: "testchannelid",
  1696  	}
  1697  
  1698  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1699  		return mgmt.GetManagerForChain("testchannelid")
  1700  	})
  1701  
  1702  	pdFactory := &pvtDataFactory{}
  1703  
  1704  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1705  
  1706  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1707  
  1708  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1709  	appCapability := &capabilitymock.AppCapabilities{}
  1710  	capabilityProvider.On("Capabilities").Return(appCapability)
  1711  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1712  	coordinator := NewCoordinator(Support{
  1713  		ChainID:            "testchannelid",
  1714  		CollectionStore:    cs,
  1715  		Committer:          committer,
  1716  		Fetcher:            fetcher,
  1717  		Validator:          &validatorMock{},
  1718  		CapabilityProvider: capabilityProvider,
  1719  	}, store.store, peerSelfSignedData, metrics, conf, idDeserializerFactory)
  1720  
  1721  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1722  	block := bf.AddTxn("tx10", "ns1", hash, "c1").create()
  1723  	block.Header.Number = 10
  1724  	pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1").create()
  1725  	// test no blocks purged yet
  1726  	assertPurged(false)
  1727  	err := coordinator.StoreBlock(block, pvtData)
  1728  	assert.NoError(t, err)
  1729  	// test first 6 blocks were purged
  1730  	assertPurged(true)
  1731  }
  1732  
  1733  func TestCoordinatorStorePvtData(t *testing.T) {
  1734  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1735  	cs := createcollectionStore(protoutil.SignedData{}).thatAcceptsAll()
  1736  	committer := &mocks.Committer{}
  1737  
  1738  	store := newTransientStore(t)
  1739  	defer store.tearDown()
  1740  
  1741  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1742  		return mgmt.GetManagerForChain("testchannelid")
  1743  	})
  1744  
  1745  	fetcher := &fetcherMock{t: t}
  1746  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1747  
  1748  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1749  	appCapability := &capabilitymock.AppCapabilities{}
  1750  	capabilityProvider.On("Capabilities").Return(appCapability)
  1751  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1752  	coordinator := NewCoordinator(Support{
  1753  		ChainID:            "testchannelid",
  1754  		CollectionStore:    cs,
  1755  		Committer:          committer,
  1756  		Fetcher:            fetcher,
  1757  		Validator:          &validatorMock{},
  1758  		CapabilityProvider: capabilityProvider,
  1759  	}, store.store, protoutil.SignedData{}, metrics, testConfig, idDeserializerFactory)
  1760  	pvtData := (&pvtDataFactory{}).addRWSet().addNSRWSet("ns1", "c1").create()
  1761  	// Green path: ledger height can be retrieved from ledger/committer
  1762  	err := coordinator.StorePvtData("tx1", &tspb.TxPvtReadWriteSetWithConfigInfo{
  1763  		PvtRwset:          pvtData[0].WriteSet,
  1764  		CollectionConfigs: make(map[string]*peer.CollectionConfigPackage),
  1765  	}, uint64(5))
  1766  	assert.NoError(t, err)
  1767  }
  1768  
  1769  func TestContainsWrites(t *testing.T) {
  1770  	// Scenario I: Nil HashedRwSet in collection
  1771  	col := &rwsetutil.CollHashedRwSet{
  1772  		CollectionName: "col1",
  1773  	}
  1774  	assert.False(t, containsWrites("tx", "ns", col))
  1775  
  1776  	// Scenario II: No writes in collection
  1777  	col.HashedRwSet = &kvrwset.HashedRWSet{}
  1778  	assert.False(t, containsWrites("tx", "ns", col))
  1779  
  1780  	// Scenario III: Some writes in collection
  1781  	col.HashedRwSet.HashedWrites = append(col.HashedRwSet.HashedWrites, &kvrwset.KVWriteHash{})
  1782  	assert.True(t, containsWrites("tx", "ns", col))
  1783  }
  1784  
  1785  func TestIgnoreReadOnlyColRWSets(t *testing.T) {
  1786  	// Scenario: The transaction has some ColRWSets that have only reads and no writes,
  1787  	// These should be ignored and not considered as missing private data that needs to be retrieved
  1788  	// from the transient store or other peers.
  1789  	// The gossip and transient store mocks in this test aren't initialized with
  1790  	// actions, so if the coordinator attempts to fetch private data from the
  1791  	// transient store or other peers, the test would fail.
  1792  	// Also - we check that at commit time - the coordinator concluded that
  1793  	// no missing private data was found.
  1794  	err := msptesttools.LoadMSPSetupForTesting()
  1795  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1796  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1797  	serializedID, err := identity.Serialize()
  1798  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1799  	data := []byte{1, 2, 3}
  1800  	signature, err := identity.Sign(data)
  1801  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1802  	peerSelfSignedData := protoutil.SignedData{
  1803  		Identity:  serializedID,
  1804  		Signature: signature,
  1805  		Data:      data,
  1806  	}
  1807  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
  1808  	var commitHappened bool
  1809  	assertCommitHappened := func() {
  1810  		assert.True(t, commitHappened)
  1811  		commitHappened = false
  1812  	}
  1813  	committer := &mocks.Committer{}
  1814  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
  1815  		blockAndPrivateData := args.Get(0).(*ledger.BlockAndPvtData)
  1816  		// Ensure there is no private data to commit
  1817  		assert.Empty(t, blockAndPrivateData.PvtData)
  1818  		// Ensure there is no missing private data
  1819  		assert.Empty(t, blockAndPrivateData.MissingPvtData)
  1820  		commitHappened = true
  1821  
  1822  		commitOpts := args.Get(1).(*ledger.CommitOptions)
  1823  		expectedCommitOpts := &ledger.CommitOptions{FetchPvtDataFromLedger: false}
  1824  		assert.Equal(t, expectedCommitOpts, commitOpts)
  1825  	}).Return(nil)
  1826  
  1827  	store := newTransientStore(t)
  1828  	defer store.tearDown()
  1829  
  1830  	fetcher := &fetcherMock{t: t}
  1831  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1832  	bf := &blockFactory{
  1833  		channelID: "testchannelid",
  1834  	}
  1835  
  1836  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1837  		return mgmt.GetManagerForChain("testchannelid")
  1838  	})
  1839  
  1840  	// The block contains a read only private data transaction
  1841  	block := bf.AddReadOnlyTxn("tx1", "ns3", hash, "c3", "c2").create()
  1842  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1843  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
  1844  
  1845  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1846  	appCapability := &capabilitymock.AppCapabilities{}
  1847  	capabilityProvider.On("Capabilities").Return(appCapability)
  1848  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1849  	coordinator := NewCoordinator(Support{
  1850  		ChainID:            "testchannelid",
  1851  		CollectionStore:    cs,
  1852  		Committer:          committer,
  1853  		Fetcher:            fetcher,
  1854  		Validator:          &validatorMock{},
  1855  		CapabilityProvider: capabilityProvider,
  1856  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1857  	// We pass a nil private data slice to indicate no pre-images though the block contains
  1858  	// private data reads.
  1859  	err = coordinator.StoreBlock(block, nil)
  1860  	assert.NoError(t, err)
  1861  	assertCommitHappened()
  1862  }
  1863  
  1864  func TestCoordinatorMetrics(t *testing.T) {
  1865  	err := msptesttools.LoadMSPSetupForTesting()
  1866  	require.NoError(t, err, fmt.Sprintf("Failed to setup local msp for testing, got err %s", err))
  1867  	identity := mspmgmt.GetLocalSigningIdentityOrPanic(factory.GetDefault())
  1868  	serializedID, err := identity.Serialize()
  1869  	require.NoError(t, err, fmt.Sprintf("Serialize should have succeeded, got err %s", err))
  1870  	data := []byte{1, 2, 3}
  1871  	signature, err := identity.Sign(data)
  1872  	require.NoError(t, err, fmt.Sprintf("Could not sign identity, got err %s", err))
  1873  	peerSelfSignedData := protoutil.SignedData{
  1874  		Identity:  serializedID,
  1875  		Signature: signature,
  1876  		Data:      data,
  1877  	}
  1878  
  1879  	cs := createcollectionStore(peerSelfSignedData).thatAcceptsAll().withMSPIdentity(identity.GetMSPIdentifier())
  1880  
  1881  	committer := &mocks.Committer{}
  1882  	committer.On("CommitLegacy", mock.Anything, mock.Anything).Return(nil)
  1883  
  1884  	store := newTransientStore(t)
  1885  	defer store.tearDown()
  1886  
  1887  	hash := util2.ComputeSHA256([]byte("rws-pre-image"))
  1888  	pdFactory := &pvtDataFactory{}
  1889  	bf := &blockFactory{
  1890  		channelID: "testchannelid",
  1891  	}
  1892  
  1893  	idDeserializerFactory := IdentityDeserializerFactoryFunc(func(chainID string) msp.IdentityDeserializer {
  1894  		return mgmt.GetManagerForChain("testchannelid")
  1895  	})
  1896  
  1897  	block := bf.AddTxnWithEndorsement("tx1", "ns1", hash, "org1", true, "c1", "c2").
  1898  		AddTxnWithEndorsement("tx2", "ns2", hash, "org2", true, "c1").
  1899  		AddTxnWithEndorsement("tx3", "ns3", hash, "org3", true, "c1").create()
  1900  
  1901  	pvtData := pdFactory.addRWSet().addNSRWSet("ns1", "c1", "c2").addRWSet().addNSRWSet("ns2", "c1").create()
  1902  	// fetch duration metric only reported when fetching from remote peer
  1903  	fetcher := &fetcherMock{t: t}
  1904  	fetcher.On("fetch", mock.Anything).expectingDigests([]privdatacommon.DigKey{
  1905  		{
  1906  			TxId: "tx3", Namespace: "ns3", Collection: "c1", BlockSeq: 1, SeqInBlock: 2,
  1907  		},
  1908  	}).Return(&privdatacommon.FetchedPvtDataContainer{
  1909  		AvailableElements: []*proto.PvtDataElement{
  1910  			{
  1911  				Digest: &proto.PvtDataDigest{
  1912  					SeqInBlock: 2,
  1913  					BlockSeq:   1,
  1914  					Collection: "c1",
  1915  					Namespace:  "ns3",
  1916  					TxId:       "tx3",
  1917  				},
  1918  				Payload: [][]byte{[]byte("rws-pre-image")},
  1919  			},
  1920  		},
  1921  	}, nil)
  1922  
  1923  	testMetricProvider := gmetricsmocks.TestUtilConstructMetricProvider()
  1924  	metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics
  1925  
  1926  	committer.On("DoesPvtDataInfoExistInLedger", mock.Anything).Return(false, nil)
  1927  
  1928  	capabilityProvider := &capabilitymock.CapabilityProvider{}
  1929  	appCapability := &capabilitymock.AppCapabilities{}
  1930  	capabilityProvider.On("Capabilities").Return(appCapability)
  1931  	appCapability.On("StorePvtDataOfInvalidTx").Return(true)
  1932  	coordinator := NewCoordinator(Support{
  1933  		ChainID:            "testchannelid",
  1934  		CollectionStore:    cs,
  1935  		Committer:          committer,
  1936  		Fetcher:            fetcher,
  1937  		Validator:          &validatorMock{},
  1938  		CapabilityProvider: capabilityProvider,
  1939  	}, store.store, peerSelfSignedData, metrics, testConfig, idDeserializerFactory)
  1940  	err = coordinator.StoreBlock(block, pvtData)
  1941  	assert.NoError(t, err)
  1942  
  1943  	// make sure all coordinator metrics were reported
  1944  
  1945  	assert.Equal(t,
  1946  		[]string{"channel", "testchannelid"},
  1947  		testMetricProvider.FakeValidationDuration.WithArgsForCall(0),
  1948  	)
  1949  	assert.True(t, testMetricProvider.FakeValidationDuration.ObserveArgsForCall(0) > 0)
  1950  	assert.Equal(t,
  1951  		[]string{"channel", "testchannelid"},
  1952  		testMetricProvider.FakeListMissingPrivateDataDuration.WithArgsForCall(0),
  1953  	)
  1954  	assert.True(t, testMetricProvider.FakeListMissingPrivateDataDuration.ObserveArgsForCall(0) > 0)
  1955  	assert.Equal(t,
  1956  		[]string{"channel", "testchannelid"},
  1957  		testMetricProvider.FakeFetchDuration.WithArgsForCall(0),
  1958  	)
  1959  	// fetch duration metric only reported when fetching from remote peer
  1960  	assert.True(t, testMetricProvider.FakeFetchDuration.ObserveArgsForCall(0) > 0)
  1961  	assert.Equal(t,
  1962  		[]string{"channel", "testchannelid"},
  1963  		testMetricProvider.FakeCommitPrivateDataDuration.WithArgsForCall(0),
  1964  	)
  1965  	assert.True(t, testMetricProvider.FakeCommitPrivateDataDuration.ObserveArgsForCall(0) > 0)
  1966  	assert.Equal(t,
  1967  		[]string{"channel", "testchannelid"},
  1968  		testMetricProvider.FakePurgeDuration.WithArgsForCall(0),
  1969  	)
  1970  	assert.True(t, testMetricProvider.FakePurgeDuration.ObserveArgsForCall(0) > 0)
  1971  }