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