711 lines
24 KiB
Go
711 lines
24 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package privdata
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
gossip2 "github.com/hyperledger/fabric-protos-go/gossip"
|
|
"github.com/hyperledger/fabric-protos-go/peer"
|
|
"github.com/hyperledger/fabric/common/metrics/disabled"
|
|
util2 "github.com/hyperledger/fabric/common/util"
|
|
"github.com/hyperledger/fabric/core/ledger"
|
|
"github.com/hyperledger/fabric/gossip/metrics"
|
|
gmetricsmocks "github.com/hyperledger/fabric/gossip/metrics/mocks"
|
|
privdatacommon "github.com/hyperledger/fabric/gossip/privdata/common"
|
|
"github.com/hyperledger/fabric/gossip/privdata/mocks"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestNoItemsToReconcile(t *testing.T) {
|
|
// Scenario: there is no missing private data to reconcile.
|
|
// reconciler should identify that we don't have missing data and it doesn't need to call reconciliationFetcher to
|
|
// fetch missing items.
|
|
// reconciler shouldn't get an error.
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
missingInfo := ledger.MissingPvtDataInfo{}
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil)
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Return(nil, errors.New("this function shouldn't be called"))
|
|
|
|
r := &Reconciler{
|
|
channel: "mychannel",
|
|
logger: logger.With("channel", "mychannel"),
|
|
metrics: metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
|
|
ReconcileSleepInterval: time.Minute,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationFetcher: fetcher, Committer: committer,
|
|
}
|
|
err := r.reconcile()
|
|
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestNotReconcilingWhenCollectionConfigNotAvailable(t *testing.T) {
|
|
// Scenario: reconciler gets an error when trying to read collection config for the missing private data.
|
|
// as a result it removes the digest slice, and there are no digests to pull.
|
|
// shouldn't get an error.
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
1: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
1: {{Collection: "col1", Namespace: "chain1"}},
|
|
},
|
|
}
|
|
|
|
var collectionConfigInfo ledger.CollectionConfigInfo
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil)
|
|
configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, errors.New("fail to get collection config"))
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
|
|
|
|
var fetchCalled bool
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
|
|
dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
|
|
require.Equal(t, 0, len(dig2CollectionConfig))
|
|
fetchCalled = true
|
|
}).Return(nil, errors.New("called with no digests"))
|
|
|
|
r := &Reconciler{
|
|
channel: "mychannel",
|
|
logger: logger.With("channel", "mychannel"),
|
|
metrics: metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
|
|
ReconcileSleepInterval: time.Minute,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationFetcher: fetcher, Committer: committer,
|
|
}
|
|
err := r.reconcile()
|
|
|
|
require.Error(t, err)
|
|
require.Equal(t, "called with no digests", err.Error())
|
|
require.True(t, fetchCalled)
|
|
}
|
|
|
|
func TestReconciliationHappyPathWithoutScheduler(t *testing.T) {
|
|
// Scenario: happy path when trying to reconcile missing private data.
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
1: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
4: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
4: {{Collection: "col1", Namespace: "ns1"}},
|
|
5: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
}
|
|
|
|
collectionConfigInfo := ledger.CollectionConfigInfo{
|
|
CollectionConfig: &peer.CollectionConfigPackage{
|
|
Config: []*peer.CollectionConfig{
|
|
{Payload: &peer.CollectionConfig_StaticCollectionConfig{
|
|
StaticCollectionConfig: &peer.StaticCollectionConfig{
|
|
Name: "col1",
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
CommittingBlockNum: 1,
|
|
}
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
|
|
missingPvtDataTracker.Mock = mock.Mock{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
|
|
})
|
|
configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
|
|
|
|
result := &privdatacommon.FetchedPvtDataContainer{}
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
|
|
dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
|
|
require.Equal(t, 3, len(dig2CollectionConfig))
|
|
for digest := range dig2CollectionConfig {
|
|
if digest.BlockSeq != 3 {
|
|
// fetch private data only for block 3. Assume that the other
|
|
// block's private data could not be fetched
|
|
continue
|
|
}
|
|
hash := util2.ComputeSHA256([]byte("rws-pre-image"))
|
|
element := &gossip2.PvtDataElement{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: digest.TxId,
|
|
BlockSeq: digest.BlockSeq,
|
|
Collection: digest.Collection,
|
|
Namespace: digest.Namespace,
|
|
SeqInBlock: digest.SeqInBlock,
|
|
},
|
|
Payload: [][]byte{hash},
|
|
}
|
|
result.AvailableElements = append(result.AvailableElements, element)
|
|
}
|
|
}).Return(result, nil)
|
|
|
|
expectedUnreconciledMissingData := ledger.MissingPvtDataInfo{
|
|
4: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
4: {{Collection: "col1", Namespace: "ns1"}},
|
|
5: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
}
|
|
|
|
var commitPvtDataOfOldBlocksHappened bool
|
|
var blockNum, seqInBlock uint64
|
|
blockNum = 3
|
|
seqInBlock = 1
|
|
committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
|
require.Len(t, args, 2)
|
|
reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
|
|
require.Equal(t, 1, len(reconciledPvtdata))
|
|
require.Equal(t, blockNum, reconciledPvtdata[0].BlockNum)
|
|
require.Equal(t, seqInBlock, reconciledPvtdata[0].WriteSets[1].SeqInBlock)
|
|
require.Equal(t, "ns1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
|
|
require.Equal(t, "col1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
|
|
commitPvtDataOfOldBlocksHappened = true
|
|
|
|
unreconciledPvtdata := args.Get(1).(ledger.MissingPvtDataInfo)
|
|
require.Equal(t, expectedUnreconciledMissingData, unreconciledPvtdata)
|
|
}).Return([]*ledger.PvtdataHashMismatch{}, nil)
|
|
|
|
testMetricProvider := gmetricsmocks.TestUtilConstructMetricProvider()
|
|
metrics := metrics.NewGossipMetrics(testMetricProvider.FakeProvider).PrivdataMetrics
|
|
|
|
r := &Reconciler{
|
|
channel: "mychannel",
|
|
logger: logger.With("channel", "mychannel"),
|
|
metrics: metrics,
|
|
ReconcileSleepInterval: time.Minute,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationFetcher: fetcher, Committer: committer,
|
|
}
|
|
err := r.reconcile()
|
|
|
|
require.NoError(t, err)
|
|
require.True(t, commitPvtDataOfOldBlocksHappened)
|
|
|
|
require.Equal(t,
|
|
[]string{"channel", "mychannel"},
|
|
testMetricProvider.FakeReconciliationDuration.WithArgsForCall(0),
|
|
)
|
|
}
|
|
|
|
func TestReconciliationHappyPathWithScheduler(t *testing.T) {
|
|
// Scenario: happy path when trying to reconcile missing private data.
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
1: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
}
|
|
|
|
collectionConfigInfo := ledger.CollectionConfigInfo{
|
|
CollectionConfig: &peer.CollectionConfigPackage{
|
|
Config: []*peer.CollectionConfig{
|
|
{Payload: &peer.CollectionConfig_StaticCollectionConfig{
|
|
StaticCollectionConfig: &peer.StaticCollectionConfig{
|
|
Name: "col1",
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
CommittingBlockNum: 1,
|
|
}
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
|
|
missingPvtDataTracker.Mock = mock.Mock{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
|
|
})
|
|
configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
|
|
|
|
result := &privdatacommon.FetchedPvtDataContainer{}
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
|
|
dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
|
|
require.Equal(t, 1, len(dig2CollectionConfig))
|
|
for digest := range dig2CollectionConfig {
|
|
hash := util2.ComputeSHA256([]byte("rws-pre-image"))
|
|
element := &gossip2.PvtDataElement{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: digest.TxId,
|
|
BlockSeq: digest.BlockSeq,
|
|
Collection: digest.Collection,
|
|
Namespace: digest.Namespace,
|
|
SeqInBlock: digest.SeqInBlock,
|
|
},
|
|
Payload: [][]byte{hash},
|
|
}
|
|
result.AvailableElements = append(result.AvailableElements, element)
|
|
}
|
|
}).Return(result, nil)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(1)
|
|
|
|
var commitPvtDataOfOldBlocksHappened bool
|
|
var blockNum, seqInBlock uint64
|
|
blockNum = 3
|
|
seqInBlock = 1
|
|
committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
|
reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
|
|
require.Equal(t, 1, len(reconciledPvtdata))
|
|
require.Equal(t, blockNum, reconciledPvtdata[0].BlockNum)
|
|
require.Equal(t, seqInBlock, reconciledPvtdata[0].WriteSets[1].SeqInBlock)
|
|
require.Equal(t, "ns1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
|
|
require.Equal(t, "col1", reconciledPvtdata[0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
|
|
commitPvtDataOfOldBlocksHappened = true
|
|
|
|
require.Nil(t, args.Get(1))
|
|
wg.Done()
|
|
}).Return([]*ledger.PvtdataHashMismatch{}, nil)
|
|
|
|
r := NewReconciler(
|
|
"",
|
|
metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
|
|
committer,
|
|
fetcher,
|
|
&PrivdataConfig{
|
|
ReconcileSleepInterval: time.Millisecond * 100,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationEnabled: true,
|
|
})
|
|
r.Start()
|
|
wg.Wait()
|
|
r.Stop()
|
|
|
|
require.True(t, commitPvtDataOfOldBlocksHappened)
|
|
}
|
|
|
|
func TestReconciliationPullingMissingPrivateDataAtOnePass(t *testing.T) {
|
|
// Scenario: define batch size to retrieve missing private data to 1
|
|
// and make sure that even though there are missing data for two blocks
|
|
// they will be reconciled with one shot.
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
4: ledger.MissingBlockPvtdataInfo{
|
|
1: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
}
|
|
|
|
collectionConfigInfo := &ledger.CollectionConfigInfo{
|
|
CollectionConfig: &peer.CollectionConfigPackage{
|
|
Config: []*peer.CollectionConfig{
|
|
{Payload: &peer.CollectionConfig_StaticCollectionConfig{
|
|
StaticCollectionConfig: &peer.StaticCollectionConfig{
|
|
Name: "col1",
|
|
},
|
|
}},
|
|
{Payload: &peer.CollectionConfig_StaticCollectionConfig{
|
|
StaticCollectionConfig: &peer.StaticCollectionConfig{
|
|
Name: "col2",
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
CommittingBlockNum: 1,
|
|
}
|
|
|
|
stopC := make(chan struct{})
|
|
nextC := make(chan struct{})
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
|
|
Return(missingInfo, nil).Run(func(_ mock.Arguments) {
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
3: ledger.MissingBlockPvtdataInfo{
|
|
2: {{Collection: "col2", Namespace: "ns2"}},
|
|
},
|
|
}
|
|
missingPvtDataTracker.Mock = mock.Mock{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
|
|
Return(missingInfo, nil).Run(func(_ mock.Arguments) {
|
|
// here we are making sure that we will first stop
|
|
// reconciliation so next call to GetMissingPvtDataInfoForMostRecentBlocks
|
|
// will go into same round
|
|
<-nextC
|
|
missingPvtDataTracker.Mock = mock.Mock{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).
|
|
Return(nil, nil)
|
|
})
|
|
// make sure we calling stop reconciliation loop, so for sure
|
|
// in this test we won't get to second round though making sure
|
|
// we are retrieving on single pass
|
|
stopC <- struct{}{}
|
|
})
|
|
|
|
configHistoryRetriever.
|
|
On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).
|
|
Return(collectionConfigInfo, nil)
|
|
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
|
|
|
|
result := &privdatacommon.FetchedPvtDataContainer{}
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
|
|
result.AvailableElements = make([]*gossip2.PvtDataElement, 0)
|
|
dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
|
|
require.Equal(t, 1, len(dig2CollectionConfig))
|
|
for digest := range dig2CollectionConfig {
|
|
hash := util2.ComputeSHA256([]byte("rws-pre-image"))
|
|
element := &gossip2.PvtDataElement{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: digest.TxId,
|
|
BlockSeq: digest.BlockSeq,
|
|
Collection: digest.Collection,
|
|
Namespace: digest.Namespace,
|
|
SeqInBlock: digest.SeqInBlock,
|
|
},
|
|
Payload: [][]byte{hash},
|
|
}
|
|
result.AvailableElements = append(result.AvailableElements, element)
|
|
}
|
|
}).Return(result, nil)
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(2)
|
|
|
|
var commitPvtDataOfOldBlocksHappened bool
|
|
pvtDataStore := make([][]*ledger.ReconciledPvtdata, 0)
|
|
committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Run(func(args mock.Arguments) {
|
|
reconciledPvtdata := args.Get(0).([]*ledger.ReconciledPvtdata)
|
|
require.Equal(t, 1, len(reconciledPvtdata))
|
|
pvtDataStore = append(pvtDataStore, reconciledPvtdata)
|
|
commitPvtDataOfOldBlocksHappened = true
|
|
|
|
require.Nil(t, args.Get(1))
|
|
wg.Done()
|
|
}).Return([]*ledger.PvtdataHashMismatch{}, nil)
|
|
|
|
r := NewReconciler(
|
|
"",
|
|
metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
|
|
committer,
|
|
fetcher,
|
|
&PrivdataConfig{
|
|
ReconcileSleepInterval: time.Millisecond * 100,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationEnabled: true,
|
|
})
|
|
r.Start()
|
|
<-stopC
|
|
r.Stop()
|
|
nextC <- struct{}{}
|
|
wg.Wait()
|
|
|
|
require.Equal(t, 2, len(pvtDataStore))
|
|
require.Equal(t, uint64(4), pvtDataStore[0][0].BlockNum)
|
|
require.Equal(t, uint64(3), pvtDataStore[1][0].BlockNum)
|
|
|
|
require.Equal(t, uint64(1), pvtDataStore[0][0].WriteSets[1].SeqInBlock)
|
|
require.Equal(t, uint64(2), pvtDataStore[1][0].WriteSets[2].SeqInBlock)
|
|
|
|
require.Equal(t, "ns1", pvtDataStore[0][0].WriteSets[1].WriteSet.NsPvtRwset[0].Namespace)
|
|
require.Equal(t, "ns2", pvtDataStore[1][0].WriteSets[2].WriteSet.NsPvtRwset[0].Namespace)
|
|
|
|
require.Equal(t, "col1", pvtDataStore[0][0].WriteSets[1].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
|
|
require.Equal(t, "col2", pvtDataStore[1][0].WriteSets[2].WriteSet.NsPvtRwset[0].CollectionPvtRwset[0].CollectionName)
|
|
|
|
require.True(t, commitPvtDataOfOldBlocksHappened)
|
|
}
|
|
|
|
func TestReconciliationFailedToCommit(t *testing.T) {
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
configHistoryRetriever := &mocks.ConfigHistoryRetriever{}
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
|
|
missingInfo := ledger.MissingPvtDataInfo{
|
|
3: map[uint64][]*ledger.MissingCollectionPvtDataInfo{
|
|
1: {{Collection: "col1", Namespace: "ns1"}},
|
|
},
|
|
}
|
|
|
|
collectionConfigInfo := ledger.CollectionConfigInfo{
|
|
CollectionConfig: &peer.CollectionConfigPackage{
|
|
Config: []*peer.CollectionConfig{
|
|
{Payload: &peer.CollectionConfig_StaticCollectionConfig{
|
|
StaticCollectionConfig: &peer.StaticCollectionConfig{
|
|
Name: "col1",
|
|
},
|
|
}},
|
|
},
|
|
},
|
|
CommittingBlockNum: 1,
|
|
}
|
|
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(missingInfo, nil).Run(func(_ mock.Arguments) {
|
|
missingPvtDataTracker.Mock = mock.Mock{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, nil)
|
|
})
|
|
configHistoryRetriever.On("MostRecentCollectionConfigBelow", mock.Anything, mock.Anything).Return(&collectionConfigInfo, nil)
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
committer.On("GetConfigHistoryRetriever").Return(configHistoryRetriever, nil)
|
|
|
|
result := &privdatacommon.FetchedPvtDataContainer{}
|
|
fetcher.On("FetchReconciledItems", mock.Anything).Run(func(args mock.Arguments) {
|
|
dig2CollectionConfig := args.Get(0).(privdatacommon.Dig2CollectionConfig)
|
|
require.Equal(t, 1, len(dig2CollectionConfig))
|
|
for digest := range dig2CollectionConfig {
|
|
hash := util2.ComputeSHA256([]byte("rws-pre-image"))
|
|
element := &gossip2.PvtDataElement{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: digest.TxId,
|
|
BlockSeq: digest.BlockSeq,
|
|
Collection: digest.Collection,
|
|
Namespace: digest.Namespace,
|
|
SeqInBlock: digest.SeqInBlock,
|
|
},
|
|
Payload: [][]byte{hash},
|
|
}
|
|
result.AvailableElements = append(result.AvailableElements, element)
|
|
}
|
|
}).Return(result, nil)
|
|
|
|
committer.On("CommitPvtDataOfOldBlocks", mock.Anything, mock.Anything).Return(nil, errors.New("failed to commit"))
|
|
|
|
r := &Reconciler{
|
|
channel: "mychannel",
|
|
logger: logger.With("channel", "mychannel"),
|
|
metrics: metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics,
|
|
ReconcileSleepInterval: time.Minute,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationFetcher: fetcher, Committer: committer,
|
|
}
|
|
err := r.reconcile()
|
|
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "failed to commit")
|
|
}
|
|
|
|
func TestFailuresWhileReconcilingMissingPvtData(t *testing.T) {
|
|
metrics := metrics.NewGossipMetrics(&disabled.Provider{}).PrivdataMetrics
|
|
committer := &mocks.Committer{}
|
|
fetcher := &mocks.ReconciliationFetcher{}
|
|
committer.On("GetMissingPvtDataTracker").Return(nil, errors.New("failed to obtain missing pvt data tracker"))
|
|
|
|
r := NewReconciler(
|
|
"",
|
|
metrics,
|
|
committer,
|
|
fetcher,
|
|
&PrivdataConfig{
|
|
ReconcileSleepInterval: time.Millisecond * 100,
|
|
ReconcileBatchSize: 1,
|
|
ReconciliationEnabled: true,
|
|
})
|
|
err := r.reconcile()
|
|
require.Error(t, err)
|
|
require.Contains(t, "failed to obtain missing pvt data tracker", err.Error())
|
|
|
|
committer.Mock = mock.Mock{}
|
|
committer.On("GetMissingPvtDataTracker").Return(nil, nil)
|
|
r = NewReconciler("", metrics, committer, fetcher,
|
|
&PrivdataConfig{ReconcileSleepInterval: time.Millisecond * 100, ReconcileBatchSize: 1, ReconciliationEnabled: true})
|
|
err = r.reconcile()
|
|
require.Error(t, err)
|
|
require.Contains(t, "got nil as MissingPvtDataTracker, exiting...", err.Error())
|
|
|
|
missingPvtDataTracker := &mocks.MissingPvtDataTracker{}
|
|
missingPvtDataTracker.On("GetMissingPvtDataInfoForMostRecentBlocks", mock.Anything).Return(nil, errors.New("failed get missing pvt data for recent blocks"))
|
|
|
|
committer.Mock = mock.Mock{}
|
|
committer.On("GetMissingPvtDataTracker").Return(missingPvtDataTracker, nil)
|
|
r = NewReconciler("", metrics, committer, fetcher,
|
|
&PrivdataConfig{ReconcileSleepInterval: time.Millisecond * 100, ReconcileBatchSize: 1, ReconciliationEnabled: true})
|
|
err = r.reconcile()
|
|
require.Error(t, err)
|
|
require.Contains(t, "failed get missing pvt data for recent blocks", err.Error())
|
|
}
|
|
|
|
func TestConstructUnreconciledMissingData(t *testing.T) {
|
|
requestedMissingData := privdatacommon.Dig2CollectionConfig{
|
|
privdatacommon.DigKey{
|
|
TxId: "tx1",
|
|
Namespace: "ns1",
|
|
Collection: "coll1",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 1,
|
|
}: nil,
|
|
privdatacommon.DigKey{
|
|
TxId: "tx1",
|
|
Namespace: "ns2",
|
|
Collection: "coll2",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 1,
|
|
}: nil,
|
|
privdatacommon.DigKey{
|
|
TxId: "tx1",
|
|
Namespace: "ns3",
|
|
Collection: "coll3",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 3,
|
|
}: nil,
|
|
privdatacommon.DigKey{
|
|
TxId: "tx2",
|
|
Namespace: "ns4",
|
|
Collection: "coll4",
|
|
BlockSeq: 4,
|
|
SeqInBlock: 4,
|
|
}: nil,
|
|
}
|
|
|
|
testCases := []struct {
|
|
description string
|
|
fetchedData []*gossip2.PvtDataElement
|
|
expectedUnreconciledMissingData ledger.MissingPvtDataInfo
|
|
}{
|
|
{
|
|
description: "none-reconciled",
|
|
fetchedData: nil,
|
|
expectedUnreconciledMissingData: ledger.MissingPvtDataInfo{
|
|
1: ledger.MissingBlockPvtdataInfo{
|
|
1: []*ledger.MissingCollectionPvtDataInfo{
|
|
{
|
|
Namespace: "ns1",
|
|
Collection: "coll1",
|
|
},
|
|
{
|
|
Namespace: "ns2",
|
|
Collection: "coll2",
|
|
},
|
|
},
|
|
3: []*ledger.MissingCollectionPvtDataInfo{
|
|
{
|
|
Namespace: "ns3",
|
|
Collection: "coll3",
|
|
},
|
|
},
|
|
},
|
|
4: ledger.MissingBlockPvtdataInfo{
|
|
4: []*ledger.MissingCollectionPvtDataInfo{
|
|
{
|
|
Namespace: "ns4",
|
|
Collection: "coll4",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
description: "all-reconciled",
|
|
fetchedData: []*gossip2.PvtDataElement{
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx1",
|
|
Namespace: "ns1",
|
|
Collection: "coll1",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 1,
|
|
},
|
|
},
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx1",
|
|
Namespace: "ns2",
|
|
Collection: "coll2",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 1,
|
|
},
|
|
},
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx1",
|
|
Namespace: "ns3",
|
|
Collection: "coll3",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 3,
|
|
},
|
|
},
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx2",
|
|
Namespace: "ns4",
|
|
Collection: "coll4",
|
|
BlockSeq: 4,
|
|
SeqInBlock: 4,
|
|
},
|
|
},
|
|
},
|
|
expectedUnreconciledMissingData: nil,
|
|
},
|
|
{
|
|
description: "some-unreconciled",
|
|
fetchedData: []*gossip2.PvtDataElement{
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx1",
|
|
Namespace: "ns1",
|
|
Collection: "coll1",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 1,
|
|
},
|
|
},
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx1",
|
|
Namespace: "ns3",
|
|
Collection: "coll3",
|
|
BlockSeq: 1,
|
|
SeqInBlock: 3,
|
|
},
|
|
},
|
|
{
|
|
Digest: &gossip2.PvtDataDigest{
|
|
TxId: "tx2",
|
|
Namespace: "ns4",
|
|
Collection: "coll4",
|
|
BlockSeq: 4,
|
|
SeqInBlock: 4,
|
|
},
|
|
},
|
|
},
|
|
expectedUnreconciledMissingData: ledger.MissingPvtDataInfo{
|
|
1: ledger.MissingBlockPvtdataInfo{
|
|
1: []*ledger.MissingCollectionPvtDataInfo{
|
|
{
|
|
Namespace: "ns2",
|
|
Collection: "coll2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.description, func(t *testing.T) {
|
|
unreconciledData := constructUnreconciledMissingData(requestedMissingData, testCase.fetchedData)
|
|
require.Equal(t, len(testCase.expectedUnreconciledMissingData), len(unreconciledData))
|
|
for blkNum, txsMissingData := range testCase.expectedUnreconciledMissingData {
|
|
for txNum, expectedUnreconciledData := range txsMissingData {
|
|
require.ElementsMatch(t, expectedUnreconciledData, unreconciledData[blkNum][txNum])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|