github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/gossip/privdata/reconcile_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package privdata
     8  
     9  import (
    10  	"errors"
    11  	"sync"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/hechain20/hechain/common/metrics/disabled"
    16  	util2 "github.com/hechain20/hechain/common/util"
    17  	"github.com/hechain20/hechain/core/ledger"
    18  	"github.com/hechain20/hechain/gossip/metrics"
    19  	gmetricsmocks "github.com/hechain20/hechain/gossip/metrics/mocks"
    20  	privdatacommon "github.com/hechain20/hechain/gossip/privdata/common"
    21  	"github.com/hechain20/hechain/gossip/privdata/mocks"
    22  	gossip2 "github.com/hyperledger/fabric-protos-go/gossip"
    23  	"github.com/hyperledger/fabric-protos-go/peer"
    24  	"github.com/stretchr/testify/mock"
    25  	"github.com/stretchr/testify/require"
    26  )
    27  
    28  func TestNoItemsToReconcile(t *testing.T) {
    29  	// Scenario: there is no missing private data to reconcile.
    30  	// reconciler should identify that we don't have missing data and it doesn't need to call reconciliationFetcher to
    31  	// fetch missing items.
    32  	// reconciler shouldn't get an error.
    33  	committer := &mocks.Committer{}
    34  	fetcher := &mocks.ReconciliationFetcher{}
    35  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
    36  	missingInfo := ledger.MissingPvtDataInfo{}
    37  
    38  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil)
    39  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
    40  	fetcher.On("FetchReconciledItems", mock.Anything).Return(nil, errors.New("this function shouldn't be called"))
    41  
    42  	r := &Reconciler{
    43  		channel:                "mychannel",
    44  		logger:                 logger.With("channel", "mychannel"),
    45  		metrics:                metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
    46  		ReconcileSleepInterval: time.Minute,
    47  		ReconcileBatchSize:     1,
    48  		ReconciliationFetcher:  fetcher, Committer: committer,
    49  	}
    50  	err := r.reconcile()
    51  
    52  	require.NoError(t, err)
    53  }
    54  
    55  func TestNotReconcilingWhenCollectionConfigNotAvailable(t *testing.T) {
    56  	// Scenario: reconciler gets an error when trying to read collection config for the missing private data.
    57  	// as a result it removes the digest slice, and there are no digests to pull.
    58  	// shouldn't get an error.
    59  	committer := &mocks.Committer{}
    60  	fetcher := &mocks.ReconciliationFetcher{}
    61  	configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
    62  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
    63  
    64  	missingInfo := ledger.MissingPvtDataInfo{
    65  		1: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
    66  			1: {{Collection: "col1", Namespace: "chain1"}},
    67  		},
    68  	}
    69  
    70  	var collectionConfigInfo ledger.CollectionConfigInfo
    71  
    72  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil)
    73  	configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, errors.New("fail to get collection config"))
    74  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
    75  	committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
    76  
    77  	var fetchCalled bool
    78  	fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
    79  		dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
    80  		require.Equal(t, 0, len(dig2CollectionConfig))
    81  		fetchCalled = true
    82  	}).Return(nil, errors.New("called with no digests"))
    83  
    84  	r := &Reconciler{
    85  		channel:                "mychannel",
    86  		logger:                 logger.With("channel", "mychannel"),
    87  		metrics:                metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
    88  		ReconcileSleepInterval: time.Minute,
    89  		ReconcileBatchSize:     1,
    90  		ReconciliationFetcher:  fetcher, Committer: committer,
    91  	}
    92  	err := r.reconcile()
    93  
    94  	require.Error(t, err)
    95  	require.Equal(t, "called with no digests", err.Error())
    96  	require.True(t, fetchCalled)
    97  }
    98  
    99  func TestReconciliationHappyPathWithoutScheduler(t *testing.T) {
   100  	// Scenario: happy path when trying to reconcile missing private data.
   101  	committer := &mocks.Committer{}
   102  	fetcher := &mocks.ReconciliationFetcher{}
   103  	configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
   104  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
   105  
   106  	missingInfo := ledger.MissingPvtDataInfo{
   107  		3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
   108  			1: {{Collection: "col1", Namespace: "ns1"}},
   109  		},
   110  		4: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
   111  			4: {{Collection: "col1", Namespace: "ns1"}},
   112  			5: {{Collection: "col1", Namespace: "ns1"}},
   113  		},
   114  	}
   115  
   116  	collectionConfigInfo := ledger.CollectionConfigInfo{
   117  		CollectionConfig: &peer.CollectionConfigPackage{
   118  			Config: []*peer.CollectionConfig{
   119  				{Payload: &peer.CollectionConfig_StaticCollectionConfig{
   120  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   121  						Name: "col1",
   122  					},
   123  				}},
   124  			},
   125  		},
   126  		CommittingBlockNum: 1,
   127  	}
   128  
   129  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
   130  		missingPvtDataTracker.Mock = mock.Mock{}
   131  		missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
   132  	})
   133  	configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
   134  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
   135  	committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
   136  
   137  	result := &privdatacommon.FetchedPvtDataContainer{}
   138  	fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
   139  		dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
   140  		require.Equal(t, 3, len(dig2CollectionConfig))
   141  		for digest := range dig2CollectionConfig {
   142  			if digest.BlockSeq != 3 {
   143  				// fetch private data only for block 3. Assume that the other
   144  				// block's private data could not be fetched
   145  				continue
   146  			}
   147  			hash := util2.ComputeSHA256([]byte("rws-pre-image"))
   148  			element := &gossip2.PvtDataElement{
   149  				Digest: &gossip2.PvtDataDigest{
   150  					TxId:       digest.TxId,
   151  					BlockSeq:   digest.BlockSeq,
   152  					Collection: digest.Collection,
   153  					Namespace:  digest.Namespace,
   154  					SeqInBlock: digest.SeqInBlock,
   155  				},
   156  				Payload: [][]byte{hash},
   157  			}
   158  			result.AvailableElements = append(result.AvailableElements, element)
   159  		}
   160  	}).Return(result, nil)
   161  
   162  	expectedUnreconciledMissingData := ledger.MissingPvtDataInfo{
   163  		4: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
   164  			4: {{Collection: "col1", Namespace: "ns1"}},
   165  			5: {{Collection: "col1", Namespace: "ns1"}},
   166  		},
   167  	}
   168  
   169  	var commitPvtDataOfOldBlocksHappened bool
   170  	var blockNum, seqInBlock uint64
   171  	blockNum = 3
   172  	seqInBlock = 1
   173  	committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   174  		require.Len(t, args, 2)
   175  		reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
   176  		require.Equal(t, 1, len(reconciledPvtdata))
   177  		require.Equal(t, blockNum, reconciledPvtdata[0].BlockNum)
   178  		require.Equal(t, seqInBlock, reconciledPvtdata[0].WriteSets[1].SeqInBlock)
   179  		require.Equal(t, "ns1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
   180  		require.Equal(t, "col1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
   181  		commitPvtDataOfOldBlocksHappened = true
   182  
   183  		unreconciledPvtdata := args.Get(1).(ledger.MissingPvtDataInfo)
   184  		require.Equal(t, expectedUnreconciledMissingData, unreconciledPvtdata)
   185  	}).Return([]*ledger.PvtdataHashMismatch{}, nil)
   186  
   187  	testMetricProvider := gmetricsmocks.TestUtilConstructMetricProvider()
   188  	metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics
   189  
   190  	r := &Reconciler{
   191  		channel:                "mychannel",
   192  		logger:                 logger.With("channel", "mychannel"),
   193  		metrics:                metrics,
   194  		ReconcileSleepInterval: time.Minute,
   195  		ReconcileBatchSize:     1,
   196  		ReconciliationFetcher:  fetcher, Committer: committer,
   197  	}
   198  	err := r.reconcile()
   199  
   200  	require.NoError(t, err)
   201  	require.True(t, commitPvtDataOfOldBlocksHappened)
   202  
   203  	require.Equal(t,
   204  		[]string{"channel", "mychannel"},
   205  		testMetricProvider.FakeReconciliationDuration.WithArgsForCall(0),
   206  	)
   207  }
   208  
   209  func TestReconciliationHappyPathWithScheduler(t *testing.T) {
   210  	// Scenario: happy path when trying to reconcile missing private data.
   211  	committer := &mocks.Committer{}
   212  	fetcher := &mocks.ReconciliationFetcher{}
   213  	configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
   214  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
   215  
   216  	missingInfo := ledger.MissingPvtDataInfo{
   217  		3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
   218  			1: {{Collection: "col1", Namespace: "ns1"}},
   219  		},
   220  	}
   221  
   222  	collectionConfigInfo := ledger.CollectionConfigInfo{
   223  		CollectionConfig: &peer.CollectionConfigPackage{
   224  			Config: []*peer.CollectionConfig{
   225  				{Payload: &peer.CollectionConfig_StaticCollectionConfig{
   226  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   227  						Name: "col1",
   228  					},
   229  				}},
   230  			},
   231  		},
   232  		CommittingBlockNum: 1,
   233  	}
   234  
   235  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
   236  		missingPvtDataTracker.Mock = mock.Mock{}
   237  		missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
   238  	})
   239  	configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
   240  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
   241  	committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
   242  
   243  	result := &privdatacommon.FetchedPvtDataContainer{}
   244  	fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
   245  		dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
   246  		require.Equal(t, 1, len(dig2CollectionConfig))
   247  		for digest := range dig2CollectionConfig {
   248  			hash := util2.ComputeSHA256([]byte("rws-pre-image"))
   249  			element := &gossip2.PvtDataElement{
   250  				Digest: &gossip2.PvtDataDigest{
   251  					TxId:       digest.TxId,
   252  					BlockSeq:   digest.BlockSeq,
   253  					Collection: digest.Collection,
   254  					Namespace:  digest.Namespace,
   255  					SeqInBlock: digest.SeqInBlock,
   256  				},
   257  				Payload: [][]byte{hash},
   258  			}
   259  			result.AvailableElements = append(result.AvailableElements, element)
   260  		}
   261  	}).Return(result, nil)
   262  
   263  	var wg sync.WaitGroup
   264  	wg.Add(1)
   265  
   266  	var commitPvtDataOfOldBlocksHappened bool
   267  	var blockNum, seqInBlock uint64
   268  	blockNum = 3
   269  	seqInBlock = 1
   270  	committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   271  		reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
   272  		require.Equal(t, 1, len(reconciledPvtdata))
   273  		require.Equal(t, blockNum, reconciledPvtdata[0].BlockNum)
   274  		require.Equal(t, seqInBlock, reconciledPvtdata[0].WriteSets[1].SeqInBlock)
   275  		require.Equal(t, "ns1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
   276  		require.Equal(t, "col1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
   277  		commitPvtDataOfOldBlocksHappened = true
   278  
   279  		require.Nil(t, args.Get(1))
   280  		wg.Done()
   281  	}).Return([]*ledger.PvtdataHashMismatch{}, nil)
   282  
   283  	r := NewReconciler(
   284  		"",
   285  		metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
   286  		committer,
   287  		fetcher,
   288  		&PrivdataConfig{
   289  			ReconcileSleepInterval: time.Millisecond * 100,
   290  			ReconcileBatchSize:     1,
   291  			ReconciliationEnabled:  true,
   292  		})
   293  	r.Start()
   294  	wg.Wait()
   295  	r.Stop()
   296  
   297  	require.True(t, commitPvtDataOfOldBlocksHappened)
   298  }
   299  
   300  func TestReconciliationPullingMissingPrivateDataAtOnePass(t *testing.T) {
   301  	// Scenario: define batch size to retrieve missing private data to 1
   302  	// and make sure that even though there are missing data for two blocks
   303  	// they will be reconciled with one shot.
   304  	committer := &mocks.Committer{}
   305  	fetcher := &mocks.ReconciliationFetcher{}
   306  	configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
   307  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
   308  
   309  	missingInfo := ledger.MissingPvtDataInfo{
   310  		4: ledger.MissingBlockPvtdataInfo{
   311  			1: {{Collection: "col1", Namespace: "ns1"}},
   312  		},
   313  	}
   314  
   315  	collectionConfigInfo := &ledger.CollectionConfigInfo{
   316  		CollectionConfig: &peer.CollectionConfigPackage{
   317  			Config: []*peer.CollectionConfig{
   318  				{Payload: &peer.CollectionConfig_StaticCollectionConfig{
   319  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   320  						Name: "col1",
   321  					},
   322  				}},
   323  				{Payload: &peer.CollectionConfig_StaticCollectionConfig{
   324  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   325  						Name: "col2",
   326  					},
   327  				}},
   328  			},
   329  		},
   330  		CommittingBlockNum: 1,
   331  	}
   332  
   333  	stopC := make(chan struct{})
   334  	nextC := make(chan struct{})
   335  
   336  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
   337  		Return(missingInfo, nil).Run(func(_ mock.Arguments) {
   338  		missingInfo := ledger.MissingPvtDataInfo{
   339  			3: ledger.MissingBlockPvtdataInfo{
   340  				2: {{Collection: "col2", Namespace: "ns2"}},
   341  			},
   342  		}
   343  		missingPvtDataTracker.Mock = mock.Mock{}
   344  		missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
   345  			Return(missingInfo, nil).Run(func(_ mock.Arguments) {
   346  			// here we are making sure that we will first stop
   347  			// reconciliation so next call to GetMissingPvtDataInfoForMostRecentBlocks
   348  			// will go into same round
   349  			<-nextC
   350  			missingPvtDataTracker.Mock = mock.Mock{}
   351  			missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
   352  				Return(nil, nil)
   353  		})
   354  		// make sure we calling stop reconciliation loop, so for sure
   355  		// in this test we won't get to second round though making sure
   356  		// we are retrieving on single pass
   357  		stopC <- struct{}{}
   358  	})
   359  
   360  	configHistoryRetriever.
   361  		On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).
   362  		Return(collectionConfigInfo, nil)
   363  
   364  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
   365  	committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
   366  
   367  	result := &privdatacommon.FetchedPvtDataContainer{}
   368  	fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
   369  		result.AvailableElements = make([]*gossip2.PvtDataElement, 0)
   370  		dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
   371  		require.Equal(t, 1, len(dig2CollectionConfig))
   372  		for digest := range dig2CollectionConfig {
   373  			hash := util2.ComputeSHA256([]byte("rws-pre-image"))
   374  			element := &gossip2.PvtDataElement{
   375  				Digest: &gossip2.PvtDataDigest{
   376  					TxId:       digest.TxId,
   377  					BlockSeq:   digest.BlockSeq,
   378  					Collection: digest.Collection,
   379  					Namespace:  digest.Namespace,
   380  					SeqInBlock: digest.SeqInBlock,
   381  				},
   382  				Payload: [][]byte{hash},
   383  			}
   384  			result.AvailableElements = append(result.AvailableElements, element)
   385  		}
   386  	}).Return(result, nil)
   387  
   388  	var wg sync.WaitGroup
   389  	wg.Add(2)
   390  
   391  	var commitPvtDataOfOldBlocksHappened bool
   392  	pvtDataStore := make([][]*ledger.ReconciledPvtdata, 0)
   393  	committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
   394  		reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
   395  		require.Equal(t, 1, len(reconciledPvtdata))
   396  		pvtDataStore = append(pvtDataStore, reconciledPvtdata)
   397  		commitPvtDataOfOldBlocksHappened = true
   398  
   399  		require.Nil(t, args.Get(1))
   400  		wg.Done()
   401  	}).Return([]*ledger.PvtdataHashMismatch{}, nil)
   402  
   403  	r := NewReconciler(
   404  		"",
   405  		metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
   406  		committer,
   407  		fetcher,
   408  		&PrivdataConfig{
   409  			ReconcileSleepInterval: time.Millisecond * 100,
   410  			ReconcileBatchSize:     1,
   411  			ReconciliationEnabled:  true,
   412  		})
   413  	r.Start()
   414  	<-stopC
   415  	r.Stop()
   416  	nextC <- struct{}{}
   417  	wg.Wait()
   418  
   419  	require.Equal(t, 2, len(pvtDataStore))
   420  	require.Equal(t, uint64(4), pvtDataStore[0][0].BlockNum)
   421  	require.Equal(t, uint64(3), pvtDataStore[1][0].BlockNum)
   422  
   423  	require.Equal(t, uint64(1), pvtDataStore[0][0].WriteSets[1].SeqInBlock)
   424  	require.Equal(t, uint64(2), pvtDataStore[1][0].WriteSets[2].SeqInBlock)
   425  
   426  	require.Equal(t, "ns1", pvtDataStore[0][0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
   427  	require.Equal(t, "ns2", pvtDataStore[1][0].WriteSets[2].WriteSet.NsPvtRwset[0].Namespace)
   428  
   429  	require.Equal(t, "col1", pvtDataStore[0][0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
   430  	require.Equal(t, "col2", pvtDataStore[1][0].WriteSets[2].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
   431  
   432  	require.True(t, commitPvtDataOfOldBlocksHappened)
   433  }
   434  
   435  func TestReconciliationFailedToCommit(t *testing.T) {
   436  	committer := &mocks.Committer{}
   437  	fetcher := &mocks.ReconciliationFetcher{}
   438  	configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
   439  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
   440  
   441  	missingInfo := ledger.MissingPvtDataInfo{
   442  		3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
   443  			1: {{Collection: "col1", Namespace: "ns1"}},
   444  		},
   445  	}
   446  
   447  	collectionConfigInfo := ledger.CollectionConfigInfo{
   448  		CollectionConfig: &peer.CollectionConfigPackage{
   449  			Config: []*peer.CollectionConfig{
   450  				{Payload: &peer.CollectionConfig_StaticCollectionConfig{
   451  					StaticCollectionConfig: &peer.StaticCollectionConfig{
   452  						Name: "col1",
   453  					},
   454  				}},
   455  			},
   456  		},
   457  		CommittingBlockNum: 1,
   458  	}
   459  
   460  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
   461  		missingPvtDataTracker.Mock = mock.Mock{}
   462  		missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
   463  	})
   464  	configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
   465  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
   466  	committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
   467  
   468  	result := &privdatacommon.FetchedPvtDataContainer{}
   469  	fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
   470  		dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
   471  		require.Equal(t, 1, len(dig2CollectionConfig))
   472  		for digest := range dig2CollectionConfig {
   473  			hash := util2.ComputeSHA256([]byte("rws-pre-image"))
   474  			element := &gossip2.PvtDataElement{
   475  				Digest: &gossip2.PvtDataDigest{
   476  					TxId:       digest.TxId,
   477  					BlockSeq:   digest.BlockSeq,
   478  					Collection: digest.Collection,
   479  					Namespace:  digest.Namespace,
   480  					SeqInBlock: digest.SeqInBlock,
   481  				},
   482  				Payload: [][]byte{hash},
   483  			}
   484  			result.AvailableElements = append(result.AvailableElements, element)
   485  		}
   486  	}).Return(result, nil)
   487  
   488  	committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Return(nil, errors.New("failed to commit"))
   489  
   490  	r := &Reconciler{
   491  		channel:                "mychannel",
   492  		logger:                 logger.With("channel", "mychannel"),
   493  		metrics:                metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
   494  		ReconcileSleepInterval: time.Minute,
   495  		ReconcileBatchSize:     1,
   496  		ReconciliationFetcher:  fetcher, Committer: committer,
   497  	}
   498  	err := r.reconcile()
   499  
   500  	require.Error(t, err)
   501  	require.Contains(t, err.Error(), "failed to commit")
   502  }
   503  
   504  func TestFailuresWhileReconcilingMissingPvtData(t *testing.T) {
   505  	metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
   506  	committer := &mocks.Committer{}
   507  	fetcher := &mocks.ReconciliationFetcher{}
   508  	committer.On("GetMissingPvtDataTracker").Return(nil, errors.New("failed to obtain missing pvt data tracker"))
   509  
   510  	r := NewReconciler(
   511  		"",
   512  		metrics,
   513  		committer,
   514  		fetcher,
   515  		&PrivdataConfig{
   516  			ReconcileSleepInterval: time.Millisecond * 100,
   517  			ReconcileBatchSize:     1,
   518  			ReconciliationEnabled:  true,
   519  		})
   520  	err := r.reconcile()
   521  	require.Error(t, err)
   522  	require.Contains(t, "failed to obtain missing pvt data tracker", err.Error())
   523  
   524  	committer.Mock = mock.Mock{}
   525  	committer.On("GetMissingPvtDataTracker").Return(nil, nil)
   526  	r = NewReconciler("", metrics, committer, fetcher,
   527  		&PrivdataConfig{ReconcileSleepInterval: time.Millisecond * 100, ReconcileBatchSize: 1, ReconciliationEnabled: true})
   528  	err = r.reconcile()
   529  	require.Error(t, err)
   530  	require.Contains(t, "got nil as MissingPvtDataTracker, exiting...", err.Error())
   531  
   532  	missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
   533  	missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, errors.New("failed get missing pvt data for recent blocks"))
   534  
   535  	committer.Mock = mock.Mock{}
   536  	committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
   537  	r = NewReconciler("", metrics, committer, fetcher,
   538  		&PrivdataConfig{ReconcileSleepInterval: time.Millisecond * 100, ReconcileBatchSize: 1, ReconciliationEnabled: true})
   539  	err = r.reconcile()
   540  	require.Error(t, err)
   541  	require.Contains(t, "failed get missing pvt data for recent blocks", err.Error())
   542  }
   543  
   544  func TestConstructUnreconciledMissingData(t *testing.T) {
   545  	requestedMissingData := privdatacommon.Dig2CollectionConfig{
   546  		privdatacommon.DigKey{
   547  			TxId:       "tx1",
   548  			Namespace:  "ns1",
   549  			Collection: "coll1",
   550  			BlockSeq:   1,
   551  			SeqInBlock: 1,
   552  		}: nil,
   553  		privdatacommon.DigKey{
   554  			TxId:       "tx1",
   555  			Namespace:  "ns2",
   556  			Collection: "coll2",
   557  			BlockSeq:   1,
   558  			SeqInBlock: 1,
   559  		}: nil,
   560  		privdatacommon.DigKey{
   561  			TxId:       "tx1",
   562  			Namespace:  "ns3",
   563  			Collection: "coll3",
   564  			BlockSeq:   1,
   565  			SeqInBlock: 3,
   566  		}: nil,
   567  		privdatacommon.DigKey{
   568  			TxId:       "tx2",
   569  			Namespace:  "ns4",
   570  			Collection: "coll4",
   571  			BlockSeq:   4,
   572  			SeqInBlock: 4,
   573  		}: nil,
   574  	}
   575  
   576  	testCases := []struct {
   577  		description                     string
   578  		fetchedData                     []*gossip2.PvtDataElement
   579  		expectedUnreconciledMissingData ledger.MissingPvtDataInfo
   580  	}{
   581  		{
   582  			description: "none-reconciled",
   583  			fetchedData: nil,
   584  			expectedUnreconciledMissingData: ledger.MissingPvtDataInfo{
   585  				1: ledger.MissingBlockPvtdataInfo{
   586  					1: []*ledger.MissingCollectionPvtDataInfo{
   587  						{
   588  							Namespace:  "ns1",
   589  							Collection: "coll1",
   590  						},
   591  						{
   592  							Namespace:  "ns2",
   593  							Collection: "coll2",
   594  						},
   595  					},
   596  					3: []*ledger.MissingCollectionPvtDataInfo{
   597  						{
   598  							Namespace:  "ns3",
   599  							Collection: "coll3",
   600  						},
   601  					},
   602  				},
   603  				4: ledger.MissingBlockPvtdataInfo{
   604  					4: []*ledger.MissingCollectionPvtDataInfo{
   605  						{
   606  							Namespace:  "ns4",
   607  							Collection: "coll4",
   608  						},
   609  					},
   610  				},
   611  			},
   612  		},
   613  		{
   614  			description: "all-reconciled",
   615  			fetchedData: []*gossip2.PvtDataElement{
   616  				{
   617  					Digest: &gossip2.PvtDataDigest{
   618  						TxId:       "tx1",
   619  						Namespace:  "ns1",
   620  						Collection: "coll1",
   621  						BlockSeq:   1,
   622  						SeqInBlock: 1,
   623  					},
   624  				},
   625  				{
   626  					Digest: &gossip2.PvtDataDigest{
   627  						TxId:       "tx1",
   628  						Namespace:  "ns2",
   629  						Collection: "coll2",
   630  						BlockSeq:   1,
   631  						SeqInBlock: 1,
   632  					},
   633  				},
   634  				{
   635  					Digest: &gossip2.PvtDataDigest{
   636  						TxId:       "tx1",
   637  						Namespace:  "ns3",
   638  						Collection: "coll3",
   639  						BlockSeq:   1,
   640  						SeqInBlock: 3,
   641  					},
   642  				},
   643  				{
   644  					Digest: &gossip2.PvtDataDigest{
   645  						TxId:       "tx2",
   646  						Namespace:  "ns4",
   647  						Collection: "coll4",
   648  						BlockSeq:   4,
   649  						SeqInBlock: 4,
   650  					},
   651  				},
   652  			},
   653  			expectedUnreconciledMissingData: nil,
   654  		},
   655  		{
   656  			description: "some-unreconciled",
   657  			fetchedData: []*gossip2.PvtDataElement{
   658  				{
   659  					Digest: &gossip2.PvtDataDigest{
   660  						TxId:       "tx1",
   661  						Namespace:  "ns1",
   662  						Collection: "coll1",
   663  						BlockSeq:   1,
   664  						SeqInBlock: 1,
   665  					},
   666  				},
   667  				{
   668  					Digest: &gossip2.PvtDataDigest{
   669  						TxId:       "tx1",
   670  						Namespace:  "ns3",
   671  						Collection: "coll3",
   672  						BlockSeq:   1,
   673  						SeqInBlock: 3,
   674  					},
   675  				},
   676  				{
   677  					Digest: &gossip2.PvtDataDigest{
   678  						TxId:       "tx2",
   679  						Namespace:  "ns4",
   680  						Collection: "coll4",
   681  						BlockSeq:   4,
   682  						SeqInBlock: 4,
   683  					},
   684  				},
   685  			},
   686  			expectedUnreconciledMissingData: ledger.MissingPvtDataInfo{
   687  				1: ledger.MissingBlockPvtdataInfo{
   688  					1: []*ledger.MissingCollectionPvtDataInfo{
   689  						{
   690  							Namespace:  "ns2",
   691  							Collection: "coll2",
   692  						},
   693  					},
   694  				},
   695  			},
   696  		},
   697  	}
   698  
   699  	for _, testCase := range testCases {
   700  		t.Run(testCase.description, func(t *testing.T) {
   701  			unreconciledData := constructUnreconciledMissingData(requestedMissingData, testCase.fetchedData)
   702  			require.Equal(t, len(testCase.expectedUnreconciledMissingData), len(unreconciledData))
   703  			for blkNum, txsMissingData := range testCase.expectedUnreconciledMissingData {
   704  				for txNum, expectedUnreconciledData := range txsMissingData {
   705  					require.ElementsMatch(t, expectedUnreconciledData, unreconciledData[blkNum][txNum])
   706  				}
   707  			}
   708  		})
   709  	}
   710  }